Merge pull request #307 from schungx/master
Better treatment of constants and doc-comments for functions.
This commit is contained in:
commit
b5879f9304
@ -6,7 +6,7 @@ members = [
|
|||||||
|
|
||||||
[package]
|
[package]
|
||||||
name = "rhai"
|
name = "rhai"
|
||||||
version = "0.19.7"
|
version = "0.19.8"
|
||||||
edition = "2018"
|
edition = "2018"
|
||||||
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
|
||||||
description = "Embedded scripting for Rust"
|
description = "Embedded scripting for Rust"
|
||||||
|
23
RELEASES.md
23
RELEASES.md
@ -1,6 +1,29 @@
|
|||||||
Rhai Release Notes
|
Rhai Release Notes
|
||||||
==================
|
==================
|
||||||
|
|
||||||
|
Version 0.19.8
|
||||||
|
==============
|
||||||
|
|
||||||
|
Bug fixes
|
||||||
|
---------
|
||||||
|
|
||||||
|
* Constants are no longer propagated by the optimizer if shadowed by a non-constant variable.
|
||||||
|
* Constants passed as the `this` parameter to Rhai functions now throws an error if assigned to.
|
||||||
|
|
||||||
|
Breaking changes
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `Engine::on_progress` now takes `u64` instead of `&u64`.
|
||||||
|
* The closure for `Engine::on_debug` now takes an additional `Position` parameter.
|
||||||
|
* `AST::iter_functions` now returns `ScriptFnMetadata`.
|
||||||
|
|
||||||
|
Enhancements
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Capturing a constant variable in a closure is now supported, with no cloning.
|
||||||
|
* Provides position info for `debug` statements.
|
||||||
|
|
||||||
|
|
||||||
Version 0.19.7
|
Version 0.19.7
|
||||||
==============
|
==============
|
||||||
|
|
||||||
|
@ -164,14 +164,19 @@ fn bench_eval_switch(bench: &mut Bencher) {
|
|||||||
let rem = 0;
|
let rem = 0;
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in range(0, 10) {
|
||||||
rem = x % 5;
|
rem = x % 10;
|
||||||
|
|
||||||
sum += switch rem {
|
sum += switch rem {
|
||||||
0 => 10,
|
0 => 10,
|
||||||
1 => 12,
|
1 => 12,
|
||||||
2 => 42,
|
2 => 42,
|
||||||
3 => 1,
|
3 => 1,
|
||||||
_ => 9
|
4 => 12,
|
||||||
|
5 => 42,
|
||||||
|
6 => 1,
|
||||||
|
7 => 12,
|
||||||
|
8 => 42,
|
||||||
|
9 => 1,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
@ -191,19 +196,18 @@ fn bench_eval_nested_if(bench: &mut Bencher) {
|
|||||||
let rem = 0;
|
let rem = 0;
|
||||||
|
|
||||||
for x in range(0, 10) {
|
for x in range(0, 10) {
|
||||||
rem = x % 5;
|
rem = x % 10;
|
||||||
|
|
||||||
sum += if rem == 0 {
|
sum += if rem == 0 { 10 }
|
||||||
10
|
else if rem == 1 { 12 }
|
||||||
} else if rem == 1 {
|
else if rem == 2 { 42 }
|
||||||
12
|
else if rem == 3 { 1 }
|
||||||
} else if rem == 2 {
|
else if rem == 4 { 12 }
|
||||||
42
|
else if rem == 5 { 42 }
|
||||||
} else if rem == 3 {
|
else if rem == 6 { 1 }
|
||||||
1
|
else if rem == 7 { 12 }
|
||||||
} else{
|
else if rem == 8 { 42 }
|
||||||
9
|
else if rem == 9 { 1 };
|
||||||
};
|
|
||||||
}
|
}
|
||||||
"#;
|
"#;
|
||||||
|
|
||||||
|
@ -75,7 +75,7 @@ The Rhai Scripting Language
|
|||||||
5. [Variables](language/variables.md)
|
5. [Variables](language/variables.md)
|
||||||
6. [Constants](language/constants.md)
|
6. [Constants](language/constants.md)
|
||||||
7. [Logic Operators](language/logic.md)
|
7. [Logic Operators](language/logic.md)
|
||||||
8. [Other Operators](language/other-op.md)
|
8. [Assignment Operators](language/assignment-op.md)
|
||||||
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)
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
{
|
{
|
||||||
"version": "0.19.7",
|
"version": "0.19.8",
|
||||||
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
|
"repoHome": "https://github.com/jonathandturner/rhai/blob/master",
|
||||||
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
|
"repoTree": "https://github.com/jonathandturner/rhai/tree/master",
|
||||||
"rootUrl": "",
|
"rootUrl": "",
|
||||||
|
@ -64,6 +64,15 @@ are spliced into the script text in order to turn on/off certain sections.
|
|||||||
For fixed script texts, the constant values can be provided in a user-defined [`Scope`] object
|
For fixed script texts, the constant values can be provided in a user-defined [`Scope`] object
|
||||||
to the [`Engine`] for use in compilation and evaluation.
|
to the [`Engine`] for use in compilation and evaluation.
|
||||||
|
|
||||||
|
### Caveat
|
||||||
|
|
||||||
|
If the [constants] are modified later on (yes, it is possible, via Rust functions),
|
||||||
|
the modified values will not show up in the optimized script.
|
||||||
|
Only the initialization values of [constants] are ever retained.
|
||||||
|
|
||||||
|
This is almost never a problem because real-world scripts seldom modify a constant,
|
||||||
|
but the possibility is always there.
|
||||||
|
|
||||||
|
|
||||||
Eager Operator Evaluations
|
Eager Operator Evaluations
|
||||||
-------------------------
|
-------------------------
|
||||||
|
@ -11,6 +11,14 @@ There are three levels of optimization: `None`, `Simple` and `Full`.
|
|||||||
(i.e. it only relies on static analysis and [built-in operators] for constant [standard types],
|
(i.e. it only relies on static analysis and [built-in operators] for constant [standard types],
|
||||||
and will not perform any external function calls).
|
and will not perform any external function calls).
|
||||||
|
|
||||||
|
However, it is important to bear in mind that _constants propagation_ is performed with the
|
||||||
|
caveat that, if [constants] are modified later on (yes, it is possible, via Rust functions),
|
||||||
|
the modified values will not show up in the optimized script. Only the initialization values
|
||||||
|
of [constants] are ever retained.
|
||||||
|
|
||||||
|
Furthermore, overriding a [built-in operator][built-in operators] in the [`Engine`] afterwards
|
||||||
|
has no effect after the optimizer replaces an expression with its calculated value.
|
||||||
|
|
||||||
* `Full` is _much_ more aggressive, _including_ calling external functions on constant arguments to determine their result.
|
* `Full` is _much_ more aggressive, _including_ calling external functions on constant arguments to determine their result.
|
||||||
One benefit to this is that many more optimization opportunities arise, especially with regards to comparison operators.
|
One benefit to this is that many more optimization opportunities arise, especially with regards to comparison operators.
|
||||||
|
|
||||||
|
@ -1,12 +1,9 @@
|
|||||||
Other Operators
|
Compound Assignment Operators
|
||||||
===============
|
=============================
|
||||||
|
|
||||||
{{#include ../links.md}}
|
{{#include ../links.md}}
|
||||||
|
|
||||||
|
|
||||||
Compound Assignment Operators
|
|
||||||
----------------------------
|
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
let number = 9;
|
let number = 9;
|
||||||
|
|
||||||
@ -64,3 +61,6 @@ my_obj += #{c:3, d:4, e:5};
|
|||||||
|
|
||||||
my_obj.len() == 5;
|
my_obj.len() == 5;
|
||||||
```
|
```
|
||||||
|
|
||||||
|
In fact, the `+` and `+=` operators are usually [overloaded][function overloading] when
|
||||||
|
something is to be _added_ to an existing type.
|
@ -61,9 +61,31 @@ r"
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Constants Can be Modified, Just Not Reassigned
|
Constants Can be Modified via Rust
|
||||||
---------------------------------------------
|
---------------------------------
|
||||||
|
|
||||||
A custom type stored as a constant can be modified via its registered API -
|
A custom type stored as a constant cannot be modified via script, but _can_ be modified via
|
||||||
being a constant only prevents it from being re-assigned or operated upon by Rhai;
|
a registered Rust function that takes a first `&mut` parameter - because there is no way for
|
||||||
mutating it via a Rust function is still allowed.
|
Rhai to know whether the Rust function modifies its argument!
|
||||||
|
|
||||||
|
```rust
|
||||||
|
const x = 42; // a constant
|
||||||
|
|
||||||
|
x.increment(); // call 'increment' defined in Rust with '&mut' first parameter
|
||||||
|
|
||||||
|
x == 43; // value of 'x' is changed!
|
||||||
|
|
||||||
|
fn double() {
|
||||||
|
this *= 2; // function squares 'this'
|
||||||
|
}
|
||||||
|
|
||||||
|
x.double(); // <- error: cannot modify constant 'this'
|
||||||
|
|
||||||
|
x == 43; // value of 'x' is unchanged by script
|
||||||
|
```
|
||||||
|
|
||||||
|
This is important to keep in mind because the script [optimizer][script optimization]
|
||||||
|
by default does _constant propagation_ as a operation.
|
||||||
|
|
||||||
|
If a constant is eventually modified by a Rust function, the optimizer will not see
|
||||||
|
the updated value and will propagate the original initialization value instead.
|
||||||
|
@ -57,24 +57,6 @@ f.call(2) == 42;
|
|||||||
```
|
```
|
||||||
|
|
||||||
|
|
||||||
Constants are Not Captured
|
|
||||||
--------------------------
|
|
||||||
|
|
||||||
Constants are never shared. Their values are simply cloned.
|
|
||||||
|
|
||||||
```rust
|
|
||||||
const x = 42; // constant variable 'x'
|
|
||||||
|
|
||||||
let f = |y| x += y; // constant 'x' is cloned and not captured
|
|
||||||
|
|
||||||
x.is_shared() == false; // 'x' is not shared
|
|
||||||
|
|
||||||
f.call(10); // the cloned copy of 'x' is changed
|
|
||||||
|
|
||||||
x == 42; // 'x' is not changed
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
Beware: Captured Variables are Truly Shared
|
Beware: Captured Variables are Truly Shared
|
||||||
------------------------------------------
|
------------------------------------------
|
||||||
|
|
||||||
|
@ -22,10 +22,12 @@ When embedding Rhai into an application, it is usually necessary to trap `print`
|
|||||||
(for logging into a tracking log, for example) with the `Engine::on_print` and `Engine::on_debug` methods:
|
(for logging into a tracking log, for example) with the `Engine::on_print` and `Engine::on_debug` methods:
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
// Any function or closure that takes an '&str' argument can be used to override
|
// Any function or closure that takes an '&str' argument can be used to override 'print'.
|
||||||
// 'print' and 'debug'
|
|
||||||
engine.on_print(|x| println!("hello: {}", x));
|
engine.on_print(|x| println!("hello: {}", x));
|
||||||
engine.on_debug(|x| println!("DEBUG: {}", x));
|
|
||||||
|
// Any function or closure that takes a '&str' and a 'Position' argument can be used to
|
||||||
|
// override 'debug'.
|
||||||
|
engine.on_debug(|x, pos| println!("DEBUG at {:?}: {}", pos, x));
|
||||||
|
|
||||||
// Example: quick-'n-dirty logging
|
// Example: quick-'n-dirty logging
|
||||||
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
||||||
@ -35,7 +37,9 @@ let log = logbook.clone();
|
|||||||
engine.on_print(move |s| log.write().unwrap().push(format!("entry: {}", s)));
|
engine.on_print(move |s| log.write().unwrap().push(format!("entry: {}", s)));
|
||||||
|
|
||||||
let log = logbook.clone();
|
let log = logbook.clone();
|
||||||
engine.on_debug(move |s| log.write().unwrap().push(format!("DEBUG: {}", s)));
|
engine.on_debug(move |s, pos| log.write().unwrap().push(
|
||||||
|
format!("DEBUG at {:?}: {}", pos, s)
|
||||||
|
));
|
||||||
|
|
||||||
// Evaluate script
|
// Evaluate script
|
||||||
engine.eval::<()>(script)?;
|
engine.eval::<()>(script)?;
|
||||||
|
@ -68,6 +68,8 @@
|
|||||||
|
|
||||||
[variable]: {{rootUrl}}/language/variables.md
|
[variable]: {{rootUrl}}/language/variables.md
|
||||||
[variables]: {{rootUrl}}/language/variables.md
|
[variables]: {{rootUrl}}/language/variables.md
|
||||||
|
[constant]: {{rootUrl}}/language/constants.md
|
||||||
|
[constants]: {{rootUrl}}/language/constants.md
|
||||||
|
|
||||||
[string]: {{rootUrl}}/language/strings-chars.md
|
[string]: {{rootUrl}}/language/strings-chars.md
|
||||||
[strings]: {{rootUrl}}/language/strings-chars.md
|
[strings]: {{rootUrl}}/language/strings-chars.md
|
||||||
|
@ -13,7 +13,7 @@ the `Engine::on_progress` method:
|
|||||||
```rust
|
```rust
|
||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
engine.on_progress(|&count| { // parameter is '&u64' - number of operations already performed
|
engine.on_progress(|count| { // parameter is number of operations already performed
|
||||||
if count % 1000 == 0 {
|
if count % 1000 == 0 {
|
||||||
println!("{}", count); // print out a progress log every 1,000 operations
|
println!("{}", count); // print out a progress log every 1,000 operations
|
||||||
}
|
}
|
||||||
|
@ -142,9 +142,7 @@ fn main() {
|
|||||||
.for_each(|f| println!("{}", f));
|
.for_each(|f| println!("{}", f));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
main_ast
|
main_ast.iter_functions().for_each(|f| println!("{}", f));
|
||||||
.iter_functions()
|
|
||||||
.for_each(|(_, _, _, _, f)| println!("{}", f));
|
|
||||||
|
|
||||||
println!();
|
println!();
|
||||||
continue;
|
continue;
|
||||||
|
146
src/ast.rs
146
src/ast.rs
@ -1,6 +1,6 @@
|
|||||||
//! Module defining the AST (abstract syntax tree).
|
//! Module defining the AST (abstract syntax tree).
|
||||||
|
|
||||||
use crate::dynamic::Union;
|
use crate::dynamic::{AccessMode, Union};
|
||||||
use crate::fn_native::shared_make_mut;
|
use crate::fn_native::shared_make_mut;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
@ -82,7 +82,9 @@ pub struct ScriptFnDef {
|
|||||||
pub params: StaticVec<ImmutableString>,
|
pub params: StaticVec<ImmutableString>,
|
||||||
/// Access to external variables.
|
/// Access to external variables.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
pub externals: crate::stdlib::collections::HashSet<ImmutableString>,
|
pub externals: Vec<ImmutableString>,
|
||||||
|
/// Comment block for function.
|
||||||
|
pub fn_comments: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ScriptFnDef {
|
impl fmt::Display for ScriptFnDef {
|
||||||
@ -105,36 +107,85 @@ impl fmt::Display for ScriptFnDef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// A type containing a script-defined function's metadata.
|
||||||
|
#[derive(Debug, Clone, Hash)]
|
||||||
|
pub struct ScriptFnMetadata {
|
||||||
|
pub comments: Vec<String>,
|
||||||
|
pub access: FnAccess,
|
||||||
|
pub fn_name: ImmutableString,
|
||||||
|
pub params: Vec<ImmutableString>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for ScriptFnMetadata {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(
|
||||||
|
f,
|
||||||
|
"{}{}({}) -> Dynamic",
|
||||||
|
if self.access.is_private() {
|
||||||
|
"private "
|
||||||
|
} else {
|
||||||
|
""
|
||||||
|
},
|
||||||
|
self.fn_name,
|
||||||
|
self.params
|
||||||
|
.iter()
|
||||||
|
.map(|p| p.as_str())
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
.join(", ")
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<ScriptFnMetadata> for &ScriptFnDef {
|
||||||
|
fn into(self) -> ScriptFnMetadata {
|
||||||
|
ScriptFnMetadata {
|
||||||
|
comments: self.fn_comments.clone(),
|
||||||
|
access: self.access,
|
||||||
|
fn_name: self.name.clone(),
|
||||||
|
params: self.params.iter().cloned().collect(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
/// Compiled AST (abstract syntax tree) of a Rhai script.
|
||||||
///
|
///
|
||||||
/// # 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>,
|
statements: Vec<Stmt>,
|
||||||
/// Script-defined functions.
|
/// Script-defined functions.
|
||||||
Shared<Module>,
|
functions: Shared<Module>,
|
||||||
);
|
}
|
||||||
|
|
||||||
impl Default for AST {
|
impl Default for AST {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self(Vec::with_capacity(16), Default::default())
|
Self {
|
||||||
|
statements: Vec::with_capacity(16),
|
||||||
|
functions: Default::default(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
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: impl Into<Shared<Module>>) -> Self {
|
pub fn new(
|
||||||
Self(statements.into_iter().collect(), lib.into())
|
statements: impl IntoIterator<Item = Stmt>,
|
||||||
|
functions: impl Into<Shared<Module>>,
|
||||||
|
) -> Self {
|
||||||
|
Self {
|
||||||
|
statements: statements.into_iter().collect(),
|
||||||
|
functions: functions.into(),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
/// Get the statements.
|
/// Get the statements.
|
||||||
#[cfg(not(feature = "internals"))]
|
#[cfg(not(feature = "internals"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn statements(&self) -> &[Stmt] {
|
pub(crate) fn statements(&self) -> &[Stmt] {
|
||||||
&self.0
|
&self.statements
|
||||||
}
|
}
|
||||||
/// _(INTERNALS)_ Get the statements.
|
/// _(INTERNALS)_ Get the statements.
|
||||||
/// Exported under the `internals` feature only.
|
/// Exported under the `internals` feature only.
|
||||||
@ -142,26 +193,26 @@ impl AST {
|
|||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn statements(&self) -> &[Stmt] {
|
pub fn statements(&self) -> &[Stmt] {
|
||||||
&self.0
|
&self.statements
|
||||||
}
|
}
|
||||||
/// Get a mutable reference to the statements.
|
/// Get a mutable reference to the statements.
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
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.statements
|
||||||
}
|
}
|
||||||
/// Get the internal shared [`Module`] containing all script-defined functions.
|
/// Get the internal shared [`Module`] containing all script-defined functions.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub(crate) fn shared_lib(&self) -> Shared<Module> {
|
pub(crate) fn shared_lib(&self) -> Shared<Module> {
|
||||||
self.1.clone()
|
self.functions.clone()
|
||||||
}
|
}
|
||||||
/// Get the internal [`Module`] containing all script-defined functions.
|
/// 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.functions
|
||||||
}
|
}
|
||||||
/// _(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.
|
||||||
@ -169,7 +220,7 @@ impl AST {
|
|||||||
#[deprecated(note = "this method is volatile and may change")]
|
#[deprecated(note = "this method is volatile and may change")]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn lib(&self) -> &Module {
|
pub fn lib(&self) -> &Module {
|
||||||
&self.1
|
&self.functions
|
||||||
}
|
}
|
||||||
/// 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.
|
||||||
@ -191,14 +242,20 @@ impl AST {
|
|||||||
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
mut filter: impl FnMut(FnNamespace, FnAccess, bool, &str, usize) -> bool,
|
||||||
) -> 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.functions, &mut filter);
|
||||||
Self(Default::default(), functions.into())
|
Self {
|
||||||
|
statements: Default::default(),
|
||||||
|
functions: 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 {
|
||||||
|
statements: self.statements.clone(),
|
||||||
|
functions: 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.
|
||||||
@ -363,21 +420,24 @@ 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,
|
||||||
) -> Self {
|
) -> Self {
|
||||||
let Self(statements, functions) = self;
|
let Self {
|
||||||
|
statements,
|
||||||
|
functions,
|
||||||
|
} = self;
|
||||||
|
|
||||||
let ast = match (statements.is_empty(), other.0.is_empty()) {
|
let ast = match (statements.is_empty(), other.statements.is_empty()) {
|
||||||
(false, false) => {
|
(false, false) => {
|
||||||
let mut statements = statements.clone();
|
let mut statements = statements.clone();
|
||||||
statements.extend(other.0.iter().cloned());
|
statements.extend(other.statements.iter().cloned());
|
||||||
statements
|
statements
|
||||||
}
|
}
|
||||||
(false, true) => statements.clone(),
|
(false, true) => statements.clone(),
|
||||||
(true, false) => other.0.clone(),
|
(true, false) => other.statements.clone(),
|
||||||
(true, true) => vec![],
|
(true, true) => vec![],
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut functions = functions.as_ref().clone();
|
let mut functions = functions.as_ref().clone();
|
||||||
functions.merge_filtered(&other.1, &mut filter);
|
functions.merge_filtered(&other.functions, &mut filter);
|
||||||
|
|
||||||
Self::new(ast, functions)
|
Self::new(ast, functions)
|
||||||
}
|
}
|
||||||
@ -438,9 +498,9 @@ 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 {
|
||||||
self.0.extend(other.0.into_iter());
|
self.statements.extend(other.statements.into_iter());
|
||||||
if !other.1.is_empty() {
|
if !other.functions.is_empty() {
|
||||||
shared_make_mut(&mut self.1).merge_filtered(&other.1, &mut filter);
|
shared_make_mut(&mut self.functions).merge_filtered(&other.functions, &mut filter);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -473,29 +533,29 @@ impl AST {
|
|||||||
&mut self,
|
&mut self,
|
||||||
filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
|
filter: impl FnMut(FnNamespace, FnAccess, &str, usize) -> bool,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
if !self.1.is_empty() {
|
if !self.functions.is_empty() {
|
||||||
shared_make_mut(&mut self.1).retain_script_functions(filter);
|
shared_make_mut(&mut self.functions).retain_script_functions(filter);
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
/// Iterate through all functions
|
/// Iterate through all functions
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn iter_functions<'a>(
|
pub fn iter_functions<'a>(&'a self) -> impl Iterator<Item = ScriptFnMetadata> + 'a {
|
||||||
&'a self,
|
self.functions
|
||||||
) -> impl Iterator<Item = (FnNamespace, FnAccess, &str, usize, &ScriptFnDef)> + 'a {
|
.iter_script_fn()
|
||||||
self.1.iter_script_fn()
|
.map(|(_, _, _, _, fn_def)| fn_def.into())
|
||||||
}
|
}
|
||||||
/// 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.functions = 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.statements = vec![];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -618,9 +678,9 @@ pub enum Stmt {
|
|||||||
/// `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<IdentX>, Option<Expr>, bool, Position),
|
||||||
/// \[`export`\] `const` id `=` expr
|
/// \[`export`\] `const` id `=` expr
|
||||||
Const(Box<Ident>, Option<Expr>, bool, Position),
|
Const(Box<IdentX>, 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`;` ... `}`
|
||||||
@ -940,10 +1000,10 @@ impl Expr {
|
|||||||
Self::FloatConstant(x, _) => (*x).into(),
|
Self::FloatConstant(x, _) => (*x).into(),
|
||||||
Self::CharConstant(x, _) => (*x).into(),
|
Self::CharConstant(x, _) => (*x).into(),
|
||||||
Self::StringConstant(x, _) => x.clone().into(),
|
Self::StringConstant(x, _) => x.clone().into(),
|
||||||
Self::FnPointer(x, _) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
|
Self::FnPointer(x, _) => Dynamic(Union::FnPtr(
|
||||||
x.clone(),
|
Box::new(FnPtr::new_unchecked(x.clone(), Default::default())),
|
||||||
Default::default(),
|
AccessMode::ReadOnly,
|
||||||
)))),
|
)),
|
||||||
Self::BoolConstant(x, _) => (*x).into(),
|
Self::BoolConstant(x, _) => (*x).into(),
|
||||||
Self::Unit(_) => ().into(),
|
Self::Unit(_) => ().into(),
|
||||||
|
|
||||||
@ -954,7 +1014,7 @@ impl Expr {
|
|||||||
x.len(),
|
x.len(),
|
||||||
));
|
));
|
||||||
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
|
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
|
||||||
Dynamic(Union::Array(Box::new(arr)))
|
Dynamic(Union::Array(Box::new(arr), AccessMode::ReadOnly))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -967,7 +1027,7 @@ impl Expr {
|
|||||||
x.iter()
|
x.iter()
|
||||||
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())),
|
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())),
|
||||||
);
|
);
|
||||||
Dynamic(Union::Map(Box::new(map)))
|
Dynamic(Union::Map(Box::new(map), AccessMode::ReadOnly))
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => return None,
|
_ => return None,
|
||||||
@ -1167,7 +1227,7 @@ mod tests {
|
|||||||
assert_eq!(size_of::<Option<crate::ast::Expr>>(), 16);
|
assert_eq!(size_of::<Option<crate::ast::Expr>>(), 16);
|
||||||
assert_eq!(size_of::<crate::ast::Stmt>(), 32);
|
assert_eq!(size_of::<crate::ast::Stmt>(), 32);
|
||||||
assert_eq!(size_of::<Option<crate::ast::Stmt>>(), 32);
|
assert_eq!(size_of::<Option<crate::ast::Stmt>>(), 32);
|
||||||
assert_eq!(size_of::<crate::Scope>(), 72);
|
assert_eq!(size_of::<crate::Scope>(), 48);
|
||||||
assert_eq!(size_of::<crate::LexError>(), 56);
|
assert_eq!(size_of::<crate::LexError>(), 56);
|
||||||
assert_eq!(size_of::<crate::ParseError>(), 16);
|
assert_eq!(size_of::<crate::ParseError>(), 16);
|
||||||
assert_eq!(size_of::<crate::EvalAltResult>(), 72);
|
assert_eq!(size_of::<crate::EvalAltResult>(), 72);
|
||||||
|
468
src/dynamic.rs
468
src/dynamic.rs
@ -116,6 +116,25 @@ impl dyn Variant {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Modes of access.
|
||||||
|
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||||
|
pub enum AccessMode {
|
||||||
|
/// Mutable.
|
||||||
|
ReadWrite,
|
||||||
|
/// Immutable.
|
||||||
|
ReadOnly,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl AccessMode {
|
||||||
|
/// Is the access type [`ReadOnly`]?
|
||||||
|
pub fn is_read_only(self) -> bool {
|
||||||
|
match self {
|
||||||
|
Self::ReadWrite => false,
|
||||||
|
Self::ReadOnly => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Dynamic type containing any value.
|
/// Dynamic type containing any value.
|
||||||
pub struct Dynamic(pub(crate) Union);
|
pub struct Dynamic(pub(crate) Union);
|
||||||
|
|
||||||
@ -123,25 +142,25 @@ pub struct Dynamic(pub(crate) Union);
|
|||||||
///
|
///
|
||||||
/// Most variants are boxed to reduce the size.
|
/// Most variants are boxed to reduce the size.
|
||||||
pub enum Union {
|
pub enum Union {
|
||||||
Unit(()),
|
Unit((), AccessMode),
|
||||||
Bool(bool),
|
Bool(bool, AccessMode),
|
||||||
Str(ImmutableString),
|
Str(ImmutableString, AccessMode),
|
||||||
Char(char),
|
Char(char, AccessMode),
|
||||||
Int(INT),
|
Int(INT, AccessMode),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Float(FLOAT),
|
Float(FLOAT, AccessMode),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Array(Box<Array>),
|
Array(Box<Array>, AccessMode),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Map(Box<Map>),
|
Map(Box<Map>, AccessMode),
|
||||||
FnPtr(Box<FnPtr>),
|
FnPtr(Box<FnPtr>, AccessMode),
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
TimeStamp(Box<Instant>),
|
TimeStamp(Box<Instant>, AccessMode),
|
||||||
|
|
||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>, AccessMode),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Shared(crate::Shared<crate::Locked<Dynamic>>),
|
Shared(crate::Shared<crate::Locked<Dynamic>>, AccessMode),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Underlying [`Variant`] read guard for [`Dynamic`].
|
/// Underlying [`Variant`] read guard for [`Dynamic`].
|
||||||
@ -236,7 +255,7 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn is_variant(&self) -> bool {
|
pub fn is_variant(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Variant(_) => true,
|
Union::Variant(_, _) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,7 +265,7 @@ impl Dynamic {
|
|||||||
pub fn is_shared(&self) -> bool {
|
pub fn is_shared(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => true,
|
Union::Shared(_, _) => true,
|
||||||
_ => false,
|
_ => false,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -272,29 +291,29 @@ impl Dynamic {
|
|||||||
/// 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 {
|
||||||
Union::Unit(_) => TypeId::of::<()>(),
|
Union::Unit(_, _) => TypeId::of::<()>(),
|
||||||
Union::Bool(_) => TypeId::of::<bool>(),
|
Union::Bool(_, _) => TypeId::of::<bool>(),
|
||||||
Union::Str(_) => TypeId::of::<ImmutableString>(),
|
Union::Str(_, _) => TypeId::of::<ImmutableString>(),
|
||||||
Union::Char(_) => TypeId::of::<char>(),
|
Union::Char(_, _) => TypeId::of::<char>(),
|
||||||
Union::Int(_) => TypeId::of::<INT>(),
|
Union::Int(_, _) => TypeId::of::<INT>(),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(_) => TypeId::of::<FLOAT>(),
|
Union::Float(_, _) => TypeId::of::<FLOAT>(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(_) => TypeId::of::<Array>(),
|
Union::Array(_, _) => TypeId::of::<Array>(),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => TypeId::of::<Map>(),
|
Union::Map(_, _) => TypeId::of::<Map>(),
|
||||||
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
Union::FnPtr(_, _) => TypeId::of::<FnPtr>(),
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_) => TypeId::of::<Instant>(),
|
Union::TimeStamp(_, _) => TypeId::of::<Instant>(),
|
||||||
|
|
||||||
Union::Variant(value) => (***value).type_id(),
|
Union::Variant(value, _) => (***value).type_id(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => (*cell.borrow()).type_id(),
|
Union::Shared(cell, _) => (*cell.borrow()).type_id(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
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`].
|
||||||
@ -305,32 +324,32 @@ impl Dynamic {
|
|||||||
/// 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 {
|
||||||
Union::Unit(_) => "()",
|
Union::Unit(_, _) => "()",
|
||||||
Union::Bool(_) => "bool",
|
Union::Bool(_, _) => "bool",
|
||||||
Union::Str(_) => "string",
|
Union::Str(_, _) => "string",
|
||||||
Union::Char(_) => "char",
|
Union::Char(_, _) => "char",
|
||||||
Union::Int(_) => type_name::<INT>(),
|
Union::Int(_, _) => type_name::<INT>(),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(_) => type_name::<FLOAT>(),
|
Union::Float(_, _) => type_name::<FLOAT>(),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(_) => "array",
|
Union::Array(_, _) => "array",
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => "map",
|
Union::Map(_, _) => "map",
|
||||||
Union::FnPtr(_) => "Fn",
|
Union::FnPtr(_, _) => "Fn",
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_) => "timestamp",
|
Union::TimeStamp(_, _) => "timestamp",
|
||||||
|
|
||||||
Union::Variant(value) => (***value).type_name(),
|
Union::Variant(value, _) => (***value).type_name(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => cell
|
Union::Shared(cell, _) => cell
|
||||||
.try_borrow()
|
.try_borrow()
|
||||||
.map(|v| (*v).type_name())
|
.map(|v| (*v).type_name())
|
||||||
.unwrap_or("<shared>"),
|
.unwrap_or("<shared>"),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => (*cell.read().unwrap()).type_name(),
|
Union::Shared(cell, _) => (*cell.read().unwrap()).type_name(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -340,17 +359,17 @@ impl Hash for Dynamic {
|
|||||||
mem::discriminant(self).hash(state);
|
mem::discriminant(self).hash(state);
|
||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Unit(_) => ().hash(state),
|
Union::Unit(_, _) => ().hash(state),
|
||||||
Union::Bool(value) => value.hash(state),
|
Union::Bool(value, _) => value.hash(state),
|
||||||
Union::Str(s) => s.hash(state),
|
Union::Str(s, _) => s.hash(state),
|
||||||
Union::Char(ch) => ch.hash(state),
|
Union::Char(ch, _) => ch.hash(state),
|
||||||
Union::Int(i) => i.hash(state),
|
Union::Int(i, _) => i.hash(state),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(f) => f.to_le_bytes().hash(state),
|
Union::Float(f, _) => f.to_le_bytes().hash(state),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(a) => (**a).hash(state),
|
Union::Array(a, _) => (**a).hash(state),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(m) => {
|
Union::Map(m, _) => {
|
||||||
let mut buf: crate::StaticVec<_> = m.iter().collect();
|
let mut buf: crate::StaticVec<_> = m.iter().collect();
|
||||||
buf.sort_by(|(a, _), (b, _)| a.cmp(b));
|
buf.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
@ -362,10 +381,10 @@ impl Hash for Dynamic {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => (*cell.borrow()).hash(state),
|
Union::Shared(cell, _) => (*cell.borrow()).hash(state),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => (*cell.read().unwrap()).hash(state),
|
Union::Shared(cell, _) => (*cell.read().unwrap()).hash(state),
|
||||||
|
|
||||||
_ => unimplemented!(),
|
_ => unimplemented!(),
|
||||||
}
|
}
|
||||||
@ -404,29 +423,29 @@ pub(crate) fn map_std_type_name(name: &str) -> &str {
|
|||||||
impl fmt::Display for Dynamic {
|
impl fmt::Display for Dynamic {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Unit(_) => write!(f, ""),
|
Union::Unit(_, _) => write!(f, ""),
|
||||||
Union::Bool(value) => fmt::Display::fmt(value, f),
|
Union::Bool(value, _) => fmt::Display::fmt(value, f),
|
||||||
Union::Str(value) => fmt::Display::fmt(value, f),
|
Union::Str(value, _) => fmt::Display::fmt(value, f),
|
||||||
Union::Char(value) => fmt::Display::fmt(value, f),
|
Union::Char(value, _) => fmt::Display::fmt(value, f),
|
||||||
Union::Int(value) => fmt::Display::fmt(value, f),
|
Union::Int(value, _) => fmt::Display::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => fmt::Display::fmt(value, f),
|
Union::Float(value, _) => fmt::Display::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
Union::Array(value, _) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => {
|
Union::Map(value, _) => {
|
||||||
f.write_str("#")?;
|
f.write_str("#")?;
|
||||||
fmt::Debug::fmt(value, f)
|
fmt::Debug::fmt(value, f)
|
||||||
}
|
}
|
||||||
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
Union::FnPtr(value, _) => fmt::Display::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_) => f.write_str("<timestamp>"),
|
Union::TimeStamp(_, _) => f.write_str("<timestamp>"),
|
||||||
|
|
||||||
Union::Variant(value) => f.write_str((*value).type_name()),
|
Union::Variant(value, _) => f.write_str((*value).type_name()),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => {
|
Union::Shared(cell, _) => {
|
||||||
if let Ok(v) = cell.try_borrow() {
|
if let Ok(v) = cell.try_borrow() {
|
||||||
fmt::Display::fmt(&*v, f)
|
fmt::Display::fmt(&*v, f)
|
||||||
} else {
|
} else {
|
||||||
@ -435,7 +454,7 @@ impl fmt::Display for Dynamic {
|
|||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => fmt::Display::fmt(&*cell.read().unwrap(), f),
|
Union::Shared(cell, _) => fmt::Display::fmt(&*cell.read().unwrap(), f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -443,29 +462,29 @@ impl fmt::Display for Dynamic {
|
|||||||
impl fmt::Debug for Dynamic {
|
impl fmt::Debug for Dynamic {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Unit(value) => fmt::Debug::fmt(value, f),
|
Union::Unit(value, _) => fmt::Debug::fmt(value, f),
|
||||||
Union::Bool(value) => fmt::Debug::fmt(value, f),
|
Union::Bool(value, _) => fmt::Debug::fmt(value, f),
|
||||||
Union::Str(value) => fmt::Debug::fmt(value, f),
|
Union::Str(value, _) => fmt::Debug::fmt(value, f),
|
||||||
Union::Char(value) => fmt::Debug::fmt(value, f),
|
Union::Char(value, _) => fmt::Debug::fmt(value, f),
|
||||||
Union::Int(value) => fmt::Debug::fmt(value, f),
|
Union::Int(value, _) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => fmt::Debug::fmt(value, f),
|
Union::Float(value, _) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
Union::Array(value, _) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => {
|
Union::Map(value, _) => {
|
||||||
f.write_str("#")?;
|
f.write_str("#")?;
|
||||||
fmt::Debug::fmt(value, f)
|
fmt::Debug::fmt(value, f)
|
||||||
}
|
}
|
||||||
Union::FnPtr(value) => fmt::Debug::fmt(value, f),
|
Union::FnPtr(value, _) => fmt::Debug::fmt(value, f),
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_) => write!(f, "<timestamp>"),
|
Union::TimeStamp(_, _) => write!(f, "<timestamp>"),
|
||||||
|
|
||||||
Union::Variant(value) => write!(f, "{}", (*value).type_name()),
|
Union::Variant(value, _) => write!(f, "{}", (*value).type_name()),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => {
|
Union::Shared(cell, _) => {
|
||||||
if let Ok(v) = cell.try_borrow() {
|
if let Ok(v) = cell.try_borrow() {
|
||||||
write!(f, "{:?} (shared)", *v)
|
write!(f, "{:?} (shared)", *v)
|
||||||
} else {
|
} else {
|
||||||
@ -474,33 +493,40 @@ impl fmt::Debug for Dynamic {
|
|||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => fmt::Debug::fmt(&*cell.read().unwrap(), f),
|
Union::Shared(cell, _) => fmt::Debug::fmt(&*cell.read().unwrap(), f),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Clone for Dynamic {
|
impl Clone for Dynamic {
|
||||||
|
/// Clone the [`Dynamic`] value.
|
||||||
|
///
|
||||||
|
/// ## WARNING
|
||||||
|
///
|
||||||
|
/// The cloned copy is marked [`AccessType::Normal`] even if the original is constant.
|
||||||
fn clone(&self) -> Self {
|
fn clone(&self) -> Self {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Unit(value) => Self(Union::Unit(value)),
|
Union::Unit(value, _) => Self(Union::Unit(value, AccessMode::ReadWrite)),
|
||||||
Union::Bool(value) => Self(Union::Bool(value)),
|
Union::Bool(value, _) => Self(Union::Bool(value, AccessMode::ReadWrite)),
|
||||||
Union::Str(ref value) => Self(Union::Str(value.clone())),
|
Union::Str(ref value, _) => Self(Union::Str(value.clone(), AccessMode::ReadWrite)),
|
||||||
Union::Char(value) => Self(Union::Char(value)),
|
Union::Char(value, _) => Self(Union::Char(value, AccessMode::ReadWrite)),
|
||||||
Union::Int(value) => Self(Union::Int(value)),
|
Union::Int(value, _) => Self(Union::Int(value, AccessMode::ReadWrite)),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => Self(Union::Float(value)),
|
Union::Float(value, _) => Self(Union::Float(value, AccessMode::ReadWrite)),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(ref value) => Self(Union::Array(value.clone())),
|
Union::Array(ref value, _) => Self(Union::Array(value.clone(), AccessMode::ReadWrite)),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
Union::Map(ref value, _) => Self(Union::Map(value.clone(), AccessMode::ReadWrite)),
|
||||||
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
Union::FnPtr(ref value, _) => Self(Union::FnPtr(value.clone(), AccessMode::ReadWrite)),
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(ref value) => Self(Union::TimeStamp(value.clone())),
|
Union::TimeStamp(ref value, _) => {
|
||||||
|
Self(Union::TimeStamp(value.clone(), AccessMode::ReadWrite))
|
||||||
|
}
|
||||||
|
|
||||||
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
Union::Variant(ref value, _) => (***value).clone_into_dynamic(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => Self(Union::Shared(cell.clone())),
|
Union::Shared(ref cell, _) => Self(Union::Shared(cell.clone(), AccessMode::ReadWrite)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -514,27 +540,96 @@ 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((), AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
/// 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, AccessMode::ReadWrite));
|
||||||
|
|
||||||
|
/// Get the [`AccessMode`] for this [`Dynamic`].
|
||||||
|
pub(crate) fn access_mode(&self) -> AccessMode {
|
||||||
|
match self.0 {
|
||||||
|
Union::Unit(_, access)
|
||||||
|
| Union::Bool(_, access)
|
||||||
|
| Union::Str(_, access)
|
||||||
|
| Union::Char(_, access)
|
||||||
|
| Union::Int(_, access)
|
||||||
|
| Union::FnPtr(_, access)
|
||||||
|
| Union::Variant(_, access) => access,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Union::Float(_, access) => access,
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(_, access) => access,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(_, access) => access,
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
Union::TimeStamp(_, access) => access,
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Union::Shared(_, access) => access,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Set the [`AccessMode`] for this [`Dynamic`].
|
||||||
|
pub(crate) fn set_access_mode(&mut self, typ: AccessMode) {
|
||||||
|
match &mut self.0 {
|
||||||
|
Union::Unit(_, access)
|
||||||
|
| Union::Bool(_, access)
|
||||||
|
| Union::Str(_, access)
|
||||||
|
| Union::Char(_, access)
|
||||||
|
| Union::Int(_, access)
|
||||||
|
| Union::FnPtr(_, access)
|
||||||
|
| Union::Variant(_, access) => *access = typ,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
Union::Float(_, access) => *access = typ,
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
|
Union::Array(_, access) => *access = typ,
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
Union::Map(_, access) => *access = typ,
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
Union::TimeStamp(_, access) => *access = typ,
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Union::Shared(_, access) => *access = typ,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/// Is this [`Dynamic`] read-only?
|
||||||
|
///
|
||||||
|
/// Constant [`Dynamic`] values are read-only. If a [`&mut Dynamic`][Dynamic] to such a constant
|
||||||
|
/// is passed to a Rust function, the function can use this information to return an error of
|
||||||
|
/// [`EvalAltResult::ErrorAssignmentToConstant`][crate::EvalAltResult::ErrorAssignmentToConstant]
|
||||||
|
/// if its value is going to be modified. This safe-guards constant values from being modified
|
||||||
|
/// from within Rust functions.
|
||||||
|
pub fn is_read_only(&self) -> bool {
|
||||||
|
match self.0 {
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Union::Shared(_, access) if access.is_read_only() => true,
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
Union::Shared(ref cell, _) => cell.borrow().access_mode().is_read_only(),
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
Union::Shared(ref cell, _) => cell.read().unwrap().access_mode().is_read_only(),
|
||||||
|
|
||||||
|
_ => self.access_mode().is_read_only(),
|
||||||
|
}
|
||||||
|
}
|
||||||
/// 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
|
||||||
@ -651,7 +746,7 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Self(Union::Variant(Box::new(boxed)))
|
Self(Union::Variant(Box::new(boxed), AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
/// Turn the [`Dynamic`] value into a shared [`Dynamic`] value backed by an [`Rc`][std::rc::Rc]`<`[`RefCell`][std::cell::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`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the `sync` feature.
|
/// or [`Arc`][std::sync::Arc]`<`[`RwLock`][std::sync::RwLock]`<`[`Dynamic`]`>>` depending on the `sync` feature.
|
||||||
@ -668,10 +763,12 @@ impl Dynamic {
|
|||||||
/// Panics under the `no_closure` feature.
|
/// Panics under the `no_closure` feature.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn into_shared(self) -> Self {
|
pub fn into_shared(self) -> Self {
|
||||||
|
let _access = self.access_mode();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Shared(..) => self,
|
Union::Shared(_, _) => self,
|
||||||
_ => Self(Union::Shared(crate::Locked::new(self).into())),
|
_ => Self(Union::Shared(crate::Locked::new(self).into(), _access)),
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -707,11 +804,11 @@ impl Dynamic {
|
|||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
Union::Shared(cell) => return cell.borrow().clone().try_cast(),
|
Union::Shared(cell, _) => return cell.borrow().clone().try_cast(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
Union::Shared(cell) => return cell.read().unwrap().clone().try_cast(),
|
Union::Shared(cell, _) => return cell.read().unwrap().clone().try_cast(),
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -721,7 +818,7 @@ impl Dynamic {
|
|||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Int(value) => unsafe_try_cast(value),
|
Union::Int(value, _) => unsafe_try_cast(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -729,35 +826,35 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Float(value) => unsafe_try_cast(value),
|
Union::Float(value, _) => unsafe_try_cast(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Bool(value) => unsafe_try_cast(value),
|
Union::Bool(value, _) => unsafe_try_cast(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Str(value) => unsafe_try_cast(value),
|
Union::Str(value, _) => unsafe_try_cast(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Str(value) => unsafe_try_cast(value.into_owned()),
|
Union::Str(value, _) => unsafe_try_cast(value.into_owned()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Char(value) => unsafe_try_cast(value),
|
Union::Char(value, _) => unsafe_try_cast(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -765,7 +862,7 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Array(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Array(value, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -773,14 +870,14 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Map(value, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::FnPtr(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::FnPtr(value, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -788,22 +885,22 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::TimeStamp(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::TimeStamp(value, _) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match self.0 {
|
return match self.0 {
|
||||||
Union::Unit(value) => unsafe_try_cast(value),
|
Union::Unit(value, _) => unsafe_try_cast(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
Union::Variant(value, _) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => unreachable!(),
|
Union::Shared(_, _) => unreachable!(),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -859,7 +956,7 @@ impl Dynamic {
|
|||||||
pub fn flatten_clone(&self) -> Self {
|
pub fn flatten_clone(&self) -> Self {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell) => {
|
Union::Shared(cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return cell.borrow().clone();
|
return cell.borrow().clone();
|
||||||
|
|
||||||
@ -879,7 +976,7 @@ impl Dynamic {
|
|||||||
pub fn flatten(self) -> Self {
|
pub fn flatten(self) -> Self {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell) => crate::fn_native::shared_try_take(cell).map_or_else(
|
Union::Shared(cell, _) => crate::fn_native::shared_try_take(cell).map_or_else(
|
||||||
|cell| {
|
|cell| {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return cell.borrow().clone();
|
return cell.borrow().clone();
|
||||||
@ -907,7 +1004,7 @@ impl Dynamic {
|
|||||||
pub fn is_locked(&self) -> bool {
|
pub fn is_locked(&self) -> bool {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref _cell) => {
|
Union::Shared(ref _cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
return _cell.try_borrow().is_err();
|
return _cell.try_borrow().is_err();
|
||||||
|
|
||||||
@ -930,7 +1027,7 @@ impl Dynamic {
|
|||||||
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => {
|
Union::Shared(ref cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow();
|
let data = cell.borrow();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -962,7 +1059,7 @@ impl Dynamic {
|
|||||||
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(ref cell) => {
|
Union::Shared(ref cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
let data = cell.borrow_mut();
|
let data = cell.borrow_mut();
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
@ -991,71 +1088,71 @@ impl Dynamic {
|
|||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Int(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Float(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Float(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Bool(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Char(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Array(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Array(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Map(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::FnPtr(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::TimeStamp(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::TimeStamp(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match &self.0 {
|
return match &self.0 {
|
||||||
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Unit(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1064,9 +1161,9 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
Union::Variant(value, _) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => None,
|
Union::Shared(_, _) => None,
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1080,65 +1177,65 @@ impl Dynamic {
|
|||||||
|
|
||||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Int(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Int(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Float(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Float(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Bool(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Bool(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Str(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Str(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Char(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Char(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Array(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Array(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Map(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::FnPtr(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::TimeStamp(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::TimeStamp(value, _) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||||
return match &mut self.0 {
|
return match &mut self.0 {
|
||||||
Union::Unit(value) => <dyn Any>::downcast_mut::<T>(value),
|
Union::Unit(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
_ => None,
|
_ => None,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@ -1147,9 +1244,9 @@ impl Dynamic {
|
|||||||
}
|
}
|
||||||
|
|
||||||
match &mut self.0 {
|
match &mut self.0 {
|
||||||
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
Union::Variant(value, _) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => None,
|
Union::Shared(_, _) => None,
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1158,9 +1255,9 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_int(&self) -> Result<INT, &'static str> {
|
pub fn as_int(&self) -> Result<INT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Int(n) => Ok(n),
|
Union::Int(n, _) => Ok(n),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1170,9 +1267,9 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Float(n) => Ok(n),
|
Union::Float(n, _) => Ok(n),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1181,9 +1278,9 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Bool(b) => Ok(b),
|
Union::Bool(b, _) => Ok(b),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1192,9 +1289,9 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_char(&self) -> Result<char, &'static str> {
|
pub fn as_char(&self) -> Result<char, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Char(n) => Ok(n),
|
Union::Char(n, _) => Ok(n),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
Union::Shared(_, _) => self.read_lock().map(|v| *v).ok_or_else(|| self.type_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1205,8 +1302,8 @@ impl Dynamic {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn as_str(&self) -> Result<&str, &'static str> {
|
pub fn as_str(&self) -> Result<&str, &'static str> {
|
||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Str(s) => Ok(s),
|
Union::Str(s, _) => Ok(s),
|
||||||
Union::FnPtr(f) => Ok(f.fn_name()),
|
Union::FnPtr(f, _) => Ok(f.fn_name()),
|
||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1223,16 +1320,16 @@ impl Dynamic {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||||
match self.0 {
|
match self.0 {
|
||||||
Union::Str(s) => Ok(s),
|
Union::Str(s, _) => Ok(s),
|
||||||
Union::FnPtr(f) => Ok(f.take_data().0),
|
Union::FnPtr(f, _) => Ok(f.take_data().0),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(cell) => {
|
Union::Shared(cell, _) => {
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
{
|
{
|
||||||
let inner = cell.borrow();
|
let inner = cell.borrow();
|
||||||
match &inner.0 {
|
match &inner.0 {
|
||||||
Union::Str(s) => Ok(s.clone()),
|
Union::Str(s, _) => Ok(s.clone()),
|
||||||
Union::FnPtr(f) => Ok(f.clone().take_data().0),
|
Union::FnPtr(f, _) => Ok(f.clone().take_data().0),
|
||||||
_ => Err((*inner).type_name()),
|
_ => Err((*inner).type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1240,8 +1337,8 @@ impl Dynamic {
|
|||||||
{
|
{
|
||||||
let inner = cell.read().unwrap();
|
let inner = cell.read().unwrap();
|
||||||
match &inner.0 {
|
match &inner.0 {
|
||||||
Union::Str(s) => Ok(s.clone()),
|
Union::Str(s, _) => Ok(s.clone()),
|
||||||
Union::FnPtr(f) => Ok(f.clone().take_data().0),
|
Union::FnPtr(f, _) => Ok(f.clone().take_data().0),
|
||||||
_ => Err((*inner).type_name()),
|
_ => Err((*inner).type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1254,56 +1351,58 @@ impl Dynamic {
|
|||||||
impl From<()> for Dynamic {
|
impl From<()> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: ()) -> Self {
|
fn from(value: ()) -> Self {
|
||||||
Self(Union::Unit(value))
|
Self(Union::Unit(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<bool> for Dynamic {
|
impl From<bool> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: bool) -> Self {
|
fn from(value: bool) -> Self {
|
||||||
Self(Union::Bool(value))
|
Self(Union::Bool(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<INT> for Dynamic {
|
impl From<INT> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: INT) -> Self {
|
fn from(value: INT) -> Self {
|
||||||
Self(Union::Int(value))
|
Self(Union::Int(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
impl From<FLOAT> for Dynamic {
|
impl From<FLOAT> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: FLOAT) -> Self {
|
fn from(value: FLOAT) -> Self {
|
||||||
Self(Union::Float(value))
|
Self(Union::Float(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<char> for Dynamic {
|
impl From<char> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: char) -> Self {
|
fn from(value: char) -> Self {
|
||||||
Self(Union::Char(value))
|
Self(Union::Char(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl<S: Into<ImmutableString>> From<S> for Dynamic {
|
impl<S: Into<ImmutableString>> From<S> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: S) -> Self {
|
fn from(value: S) -> Self {
|
||||||
Self(Union::Str(value.into()))
|
Self(Union::Str(value.into(), AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
impl<T: Variant + Clone> From<crate::stdlib::vec::Vec<T>> for Dynamic {
|
impl<T: Variant + Clone> From<crate::stdlib::vec::Vec<T>> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: crate::stdlib::vec::Vec<T>) -> Self {
|
fn from(value: crate::stdlib::vec::Vec<T>) -> Self {
|
||||||
Self(Union::Array(Box::new(
|
Self(Union::Array(
|
||||||
value.into_iter().map(Dynamic::from).collect(),
|
Box::new(value.into_iter().map(Dynamic::from).collect()),
|
||||||
)))
|
AccessMode::ReadWrite,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: &[T]) -> Self {
|
fn from(value: &[T]) -> Self {
|
||||||
Self(Union::Array(Box::new(
|
Self(Union::Array(
|
||||||
value.iter().cloned().map(Dynamic::from).collect(),
|
Box::new(value.iter().cloned().map(Dynamic::from).collect()),
|
||||||
)))
|
AccessMode::ReadWrite,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -1312,30 +1411,33 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<crate::stdlib::collectio
|
|||||||
{
|
{
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: crate::stdlib::collections::HashMap<K, T>) -> Self {
|
fn from(value: crate::stdlib::collections::HashMap<K, T>) -> Self {
|
||||||
Self(Union::Map(Box::new(
|
Self(Union::Map(
|
||||||
value
|
Box::new(
|
||||||
.into_iter()
|
value
|
||||||
.map(|(k, v)| (k.into(), Dynamic::from(v)))
|
.into_iter()
|
||||||
.collect(),
|
.map(|(k, v)| (k.into(), Dynamic::from(v)))
|
||||||
)))
|
.collect(),
|
||||||
|
),
|
||||||
|
AccessMode::ReadWrite,
|
||||||
|
))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<FnPtr> for Dynamic {
|
impl From<FnPtr> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: FnPtr) -> Self {
|
fn from(value: FnPtr) -> Self {
|
||||||
Self(Union::FnPtr(Box::new(value)))
|
Self(Union::FnPtr(Box::new(value), AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
impl From<Box<FnPtr>> for Dynamic {
|
impl From<Box<FnPtr>> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: Box<FnPtr>) -> Self {
|
fn from(value: Box<FnPtr>) -> Self {
|
||||||
Self(Union::FnPtr(value))
|
Self(Union::FnPtr(value, AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
impl From<Instant> for Dynamic {
|
impl From<Instant> for Dynamic {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn from(value: Instant) -> Self {
|
fn from(value: Instant) -> Self {
|
||||||
Self(Union::TimeStamp(Box::new(value)))
|
Self(Union::TimeStamp(Box::new(value), AccessMode::ReadWrite))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
363
src/engine.rs
363
src/engine.rs
@ -1,14 +1,16 @@
|
|||||||
//! 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, AccessMode, Union, Variant};
|
||||||
use crate::fn_call::run_builtin_op_assignment;
|
use crate::fn_call::run_builtin_op_assignment;
|
||||||
use crate::fn_native::{CallableFunction, Callback, IteratorFn, OnVarCallback};
|
use crate::fn_native::{
|
||||||
|
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnProgressCallback,
|
||||||
|
OnVarCallback,
|
||||||
|
};
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||||
use crate::scope::EntryType as ScopeEntryType;
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
@ -623,11 +625,11 @@ pub struct Engine {
|
|||||||
pub(crate) resolve_var: Option<OnVarCallback>,
|
pub(crate) resolve_var: Option<OnVarCallback>,
|
||||||
|
|
||||||
/// Callback closure for implementing the `print` command.
|
/// Callback closure for implementing the `print` command.
|
||||||
pub(crate) print: Callback<str, ()>,
|
pub(crate) print: OnPrintCallback,
|
||||||
/// Callback closure for implementing the `debug` command.
|
/// Callback closure for implementing the `debug` command.
|
||||||
pub(crate) debug: Callback<str, ()>,
|
pub(crate) debug: OnDebugCallback,
|
||||||
/// Callback closure for progress reporting.
|
/// Callback closure for progress reporting.
|
||||||
pub(crate) progress: Option<Callback<u64, Option<Dynamic>>>,
|
pub(crate) progress: Option<OnProgressCallback>,
|
||||||
|
|
||||||
/// Optimize the AST after compilation.
|
/// Optimize the AST after compilation.
|
||||||
pub(crate) optimization_level: OptimizationLevel,
|
pub(crate) optimization_level: OptimizationLevel,
|
||||||
@ -676,7 +678,7 @@ pub fn is_anonymous_fn(fn_name: &str) -> bool {
|
|||||||
fn_name.starts_with(FN_ANONYMOUS)
|
fn_name.starts_with(FN_ANONYMOUS)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Print/debug to stdout
|
/// Print to stdout
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn default_print(_s: &str) {
|
fn default_print(_s: &str) {
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
@ -684,6 +686,14 @@ fn default_print(_s: &str) {
|
|||||||
println!("{}", _s);
|
println!("{}", _s);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Debug to stdout
|
||||||
|
#[inline(always)]
|
||||||
|
fn default_debug(_s: &str, _pos: Position) {
|
||||||
|
#[cfg(not(feature = "no_std"))]
|
||||||
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
|
println!("{:?} | {}", _pos, _s);
|
||||||
|
}
|
||||||
|
|
||||||
/// Search for a module within an imports stack.
|
/// Search for a module within an imports stack.
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::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(
|
||||||
@ -740,7 +750,7 @@ impl Engine {
|
|||||||
|
|
||||||
// default print/debug implementations
|
// default print/debug implementations
|
||||||
print: Box::new(default_print),
|
print: Box::new(default_print),
|
||||||
debug: Box::new(default_print),
|
debug: Box::new(default_debug),
|
||||||
|
|
||||||
// progress callback
|
// progress callback
|
||||||
progress: None,
|
progress: None,
|
||||||
@ -796,7 +806,7 @@ impl Engine {
|
|||||||
resolve_var: None,
|
resolve_var: None,
|
||||||
|
|
||||||
print: Box::new(|_| {}),
|
print: Box::new(|_| {}),
|
||||||
debug: Box::new(|_| {}),
|
debug: Box::new(|_, _| {}),
|
||||||
progress: None,
|
progress: None,
|
||||||
|
|
||||||
optimization_level: if cfg!(feature = "no_optimize") {
|
optimization_level: if cfg!(feature = "no_optimize") {
|
||||||
@ -833,7 +843,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
|
||||||
match expr {
|
match expr {
|
||||||
Expr::Variable(v) => match v.as_ref() {
|
Expr::Variable(v) => match v.as_ref() {
|
||||||
// Qualified variable
|
// Qualified variable
|
||||||
@ -850,7 +860,9 @@ impl Engine {
|
|||||||
})?;
|
})?;
|
||||||
|
|
||||||
// Module variables are constant
|
// Module variables are constant
|
||||||
Ok((target.clone().into(), name, ScopeEntryType::Constant, *pos))
|
let mut target = target.clone();
|
||||||
|
target.set_access_mode(AccessMode::ReadOnly);
|
||||||
|
Ok((target.into(), name, *pos))
|
||||||
}
|
}
|
||||||
// Normal variable access
|
// Normal variable access
|
||||||
_ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
|
_ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
|
||||||
@ -868,7 +880,7 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &'a Expr,
|
expr: &'a Expr,
|
||||||
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
|
||||||
let (index, _, _, IdentX { name, pos }) = match expr {
|
let (index, _, _, IdentX { name, pos }) = match expr {
|
||||||
Expr::Variable(v) => v.as_ref(),
|
Expr::Variable(v) => v.as_ref(),
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
@ -877,7 +889,7 @@ impl Engine {
|
|||||||
// Check if the variable is `this`
|
// Check if the variable is `this`
|
||||||
if name.as_str() == KEYWORD_THIS {
|
if name.as_str() == KEYWORD_THIS {
|
||||||
if let Some(val) = this_ptr {
|
if let Some(val) = this_ptr {
|
||||||
return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos));
|
return Ok(((*val).into(), KEYWORD_THIS, *pos));
|
||||||
} else {
|
} else {
|
||||||
return EvalAltResult::ErrorUnboundThis(*pos).into();
|
return EvalAltResult::ErrorUnboundThis(*pos).into();
|
||||||
}
|
}
|
||||||
@ -901,10 +913,11 @@ impl Engine {
|
|||||||
this_ptr,
|
this_ptr,
|
||||||
level: 0,
|
level: 0,
|
||||||
};
|
};
|
||||||
if let Some(result) =
|
if let Some(mut result) =
|
||||||
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
|
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
|
||||||
{
|
{
|
||||||
return Ok((result.into(), name, ScopeEntryType::Constant, *pos));
|
result.set_access_mode(AccessMode::ReadOnly);
|
||||||
|
return Ok((result.into(), name, *pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -918,7 +931,7 @@ impl Engine {
|
|||||||
.0
|
.0
|
||||||
};
|
};
|
||||||
|
|
||||||
let (val, typ) = scope.get_mut(index);
|
let val = scope.get_mut(index);
|
||||||
|
|
||||||
// Check for data race - probably not necessary because the only place it should conflict is in a method call
|
// Check for data race - probably not necessary because the only place it should conflict is in a method call
|
||||||
// when the object variable is also used as a parameter.
|
// when the object variable is also used as a parameter.
|
||||||
@ -926,7 +939,7 @@ impl Engine {
|
|||||||
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
|
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
|
||||||
// }
|
// }
|
||||||
|
|
||||||
Ok((val.into(), name, typ, *pos))
|
Ok((val.into(), name, *pos))
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Chain-evaluate a dot/index chain.
|
/// Chain-evaluate a dot/index chain.
|
||||||
@ -1012,8 +1025,8 @@ impl Engine {
|
|||||||
let args = &mut [target_val, &mut idx_val2, &mut new_val.0];
|
let args = &mut [target_val, &mut idx_val2, &mut new_val.0];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, 0, args, is_ref, true, false, None,
|
mods, state, lib, FN_IDX_SET, 0, args, is_ref, true, false,
|
||||||
None, level,
|
new_val.1, None, None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
@ -1057,9 +1070,8 @@ impl Engine {
|
|||||||
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,
|
||||||
level,
|
*pos, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
|
||||||
}
|
}
|
||||||
// xxx.module::fn_name(...) - syntax error
|
// xxx.module::fn_name(...) - syntax error
|
||||||
Expr::FnCall(_, _) => unreachable!(),
|
Expr::FnCall(_, _) => unreachable!(),
|
||||||
@ -1090,8 +1102,8 @@ impl Engine {
|
|||||||
let mut new_val = new_val;
|
let mut new_val = new_val;
|
||||||
let mut args = [target_val, &mut new_val.as_mut().unwrap().0];
|
let mut args = [target_val, &mut new_val.as_mut().unwrap().0];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, 0, &mut args, is_ref, true, false, None,
|
mods, state, lib, setter, 0, &mut args, is_ref, true, false, *pos,
|
||||||
None, level,
|
None, None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.map(|(v, _)| (v, true))
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
@ -1101,8 +1113,8 @@ impl Engine {
|
|||||||
let ((getter, _), IdentX { pos, .. }) = x.as_ref();
|
let ((getter, _), IdentX { pos, .. }) = x.as_ref();
|
||||||
let mut args = [target_val];
|
let mut args = [target_val];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, 0, &mut args, is_ref, true, false, None,
|
mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos,
|
||||||
None, level,
|
None, None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map(|(v, _)| (v, false))
|
||||||
.map_err(|err| err.fill_position(*pos))
|
.map_err(|err| err.fill_position(*pos))
|
||||||
@ -1129,12 +1141,10 @@ impl Engine {
|
|||||||
} = x.as_ref();
|
} = x.as_ref();
|
||||||
let def_value = def_value.as_ref();
|
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(
|
mods, state, lib, name, *hash, target, args, def_value,
|
||||||
mods, state, lib, name, *hash, target, args, def_value,
|
*native, false, *pos, level,
|
||||||
*native, false, level,
|
)?;
|
||||||
)
|
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
|
||||||
val.into()
|
val.into()
|
||||||
}
|
}
|
||||||
// {xxx:map}.module::fn_name(...) - syntax error
|
// {xxx:map}.module::fn_name(...) - syntax error
|
||||||
@ -1160,7 +1170,7 @@ impl Engine {
|
|||||||
let (mut val, updated) = self
|
let (mut val, updated) = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
mods, state, lib, getter, 0, args, is_ref, true, false,
|
mods, state, lib, getter, 0, args, is_ref, true, false,
|
||||||
None, None, level,
|
*pos, None, None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
.map_err(|err| err.fill_position(*pos))?;
|
||||||
|
|
||||||
@ -1187,7 +1197,7 @@ impl Engine {
|
|||||||
arg_values[1] = val;
|
arg_values[1] = val;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, 0, arg_values, is_ref, true,
|
mods, state, lib, setter, 0, arg_values, is_ref, true,
|
||||||
false, None, None, level,
|
false, *pos, None, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -1213,12 +1223,10 @@ impl Engine {
|
|||||||
} = f.as_ref();
|
} = f.as_ref();
|
||||||
let def_value = def_value.as_ref();
|
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(
|
mods, state, lib, name, *hash, target, args, def_value,
|
||||||
mods, state, lib, name, *hash, target, args, def_value,
|
*native, false, *pos, level,
|
||||||
*native, false, level,
|
)?;
|
||||||
)
|
|
||||||
.map_err(|err| err.fill_position(*pos))?;
|
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
let target = &mut val.into();
|
let target = &mut val.into();
|
||||||
|
|
||||||
@ -1279,16 +1287,13 @@ impl Engine {
|
|||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(*var_pos))?;
|
.map_err(|err| err.fill_position(*var_pos))?;
|
||||||
|
|
||||||
let (target, _, typ, pos) =
|
let (target, _, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs)?;
|
||||||
|
|
||||||
// Constants cannot be modified
|
// Constants cannot be modified
|
||||||
match typ {
|
if target.as_ref().is_read_only() && new_val.is_some() {
|
||||||
ScopeEntryType::Constant if new_val.is_some() => {
|
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
||||||
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
.into();
|
||||||
.into();
|
|
||||||
}
|
|
||||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
let obj_ptr = &mut target.into();
|
let obj_ptr = &mut target.into();
|
||||||
@ -1410,7 +1415,7 @@ impl Engine {
|
|||||||
|
|
||||||
match target {
|
match target {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(arr)) => {
|
Dynamic(Union::Array(arr, _)) => {
|
||||||
// val_array[idx]
|
// val_array[idx]
|
||||||
let index = idx
|
let index = idx
|
||||||
.as_int()
|
.as_int()
|
||||||
@ -1430,7 +1435,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(map)) => {
|
Dynamic(Union::Map(map, _)) => {
|
||||||
// val_map[idx]
|
// val_map[idx]
|
||||||
Ok(if _create {
|
Ok(if _create {
|
||||||
let index = idx.take_immutable_string().map_err(|err| {
|
let index = idx.take_immutable_string().map_err(|err| {
|
||||||
@ -1450,7 +1455,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Str(s)) => {
|
Dynamic(Union::Str(s, _)) => {
|
||||||
// val_string[idx]
|
// val_string[idx]
|
||||||
let chars_len = s.chars().count();
|
let chars_len = s.chars().count();
|
||||||
let index = idx
|
let index = idx
|
||||||
@ -1474,8 +1479,8 @@ impl Engine {
|
|||||||
let mut idx = idx;
|
let mut idx = idx;
|
||||||
let args = &mut [target, &mut idx];
|
let args = &mut [target, &mut idx];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
_mods, state, _lib, FN_IDX_GET, 0, args, _is_ref, true, false, None, None,
|
_mods, state, _lib, FN_IDX_GET, 0, args, _is_ref, true, false, idx_pos, None,
|
||||||
_level,
|
None, _level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
@ -1517,7 +1522,7 @@ impl Engine {
|
|||||||
|
|
||||||
match rhs_value {
|
match rhs_value {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
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();
|
let def_value = def_value.as_ref();
|
||||||
@ -1529,11 +1534,12 @@ impl Engine {
|
|||||||
let hash =
|
let hash =
|
||||||
calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id()));
|
calc_native_fn_hash(empty(), OP_EQUALS, args.iter().map(|a| a.type_id()));
|
||||||
|
|
||||||
|
let pos = rhs.position();
|
||||||
|
|
||||||
if self
|
if self
|
||||||
.call_native_fn(
|
.call_native_fn(
|
||||||
mods, state, lib, OP_EQUALS, hash, args, false, false, def_value,
|
mods, state, lib, OP_EQUALS, hash, args, false, false, pos, def_value,
|
||||||
)
|
)?
|
||||||
.map_err(|err| err.fill_position(rhs.position()))?
|
|
||||||
.0
|
.0
|
||||||
.as_bool()
|
.as_bool()
|
||||||
.unwrap_or(false)
|
.unwrap_or(false)
|
||||||
@ -1545,16 +1551,16 @@ impl Engine {
|
|||||||
Ok(false.into())
|
Ok(false.into())
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(rhs_value)) => match lhs_value {
|
Dynamic(Union::Map(rhs_value, _)) => match lhs_value {
|
||||||
// Only allows string or char
|
// Only allows string or char
|
||||||
Dynamic(Union::Str(s)) => Ok(rhs_value.contains_key(&s).into()),
|
Dynamic(Union::Str(s, _)) => Ok(rhs_value.contains_key(&s).into()),
|
||||||
Dynamic(Union::Char(c)) => Ok(rhs_value.contains_key(&c.to_string()).into()),
|
Dynamic(Union::Char(c, _)) => Ok(rhs_value.contains_key(&c.to_string()).into()),
|
||||||
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
|
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
|
||||||
},
|
},
|
||||||
Dynamic(Union::Str(rhs_value)) => match lhs_value {
|
Dynamic(Union::Str(rhs_value, _)) => match lhs_value {
|
||||||
// Only allows string or char
|
// Only allows string or char
|
||||||
Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()),
|
Dynamic(Union::Str(s, _)) => Ok(rhs_value.contains(s.as_str()).into()),
|
||||||
Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()),
|
Dynamic(Union::Char(c, _)) => Ok(rhs_value.contains(c).into()),
|
||||||
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
|
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
|
||||||
},
|
},
|
||||||
_ => EvalAltResult::ErrorInExpr(rhs.position()).into(),
|
_ => EvalAltResult::ErrorInExpr(rhs.position()).into(),
|
||||||
@ -1570,23 +1576,21 @@ impl Engine {
|
|||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
no_const: bool,
|
_no_const: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
|
) -> Result<(Target<'s>, Position), Box<EvalAltResult>> {
|
||||||
match expr {
|
match expr {
|
||||||
// var - point directly to the value
|
// var - point directly to the value
|
||||||
Expr::Variable(_) => {
|
Expr::Variable(_) => {
|
||||||
let (target, _, typ, pos) =
|
let (mut target, _, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||||
|
|
||||||
Ok((
|
// If necessary, constants are cloned
|
||||||
match typ {
|
if target.as_ref().is_read_only() {
|
||||||
// If necessary, constants are cloned
|
target = target.into_owned();
|
||||||
ScopeEntryType::Constant if no_const => target.into_owned(),
|
}
|
||||||
_ => target,
|
|
||||||
},
|
Ok((target, pos))
|
||||||
pos,
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
// var[...]
|
// var[...]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
@ -1601,7 +1605,7 @@ impl Engine {
|
|||||||
let idx = self.eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)?;
|
let idx = self.eval_expr(scope, mods, state, lib, this_ptr, &x.rhs, level)?;
|
||||||
let idx_pos = x.rhs.position();
|
let idx_pos = x.rhs.position();
|
||||||
let (mut target, pos) = self.eval_expr_as_target(
|
let (mut target, pos) = self.eval_expr_as_target(
|
||||||
scope, mods, state, lib, this_ptr, &x.lhs, no_const, level,
|
scope, mods, state, lib, this_ptr, &x.lhs, _no_const, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
@ -1628,7 +1632,7 @@ impl Engine {
|
|||||||
// var.prop
|
// var.prop
|
||||||
Expr::Property(ref p) => {
|
Expr::Property(ref p) => {
|
||||||
let (mut target, _) = self.eval_expr_as_target(
|
let (mut target, _) = self.eval_expr_as_target(
|
||||||
scope, mods, state, lib, this_ptr, &x.lhs, no_const, level,
|
scope, mods, state, lib, this_ptr, &x.lhs, _no_const, level,
|
||||||
)?;
|
)?;
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
|
|
||||||
@ -1655,11 +1659,10 @@ impl Engine {
|
|||||||
let ((getter, _), IdentX { pos, .. }) = p.as_ref();
|
let ((getter, _), IdentX { pos, .. }) = p.as_ref();
|
||||||
let mut args = [target.as_mut()];
|
let mut args = [target.as_mut()];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, 0, &mut args, is_ref, true, false, None,
|
mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos,
|
||||||
None, level,
|
None, None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v.into(), *pos))
|
.map(|(v, _)| (v.into(), *pos))
|
||||||
.map_err(|err| err.fill_position(*pos))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// var.???
|
// var.???
|
||||||
@ -1706,8 +1709,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Expr::Variable(_) => {
|
Expr::Variable(_) => {
|
||||||
let (val, _, _, _) =
|
let (val, _, _) = self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
|
||||||
Ok(val.take_or_clone())
|
Ok(val.take_or_clone())
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!(),
|
Expr::Property(_) => unreachable!(),
|
||||||
@ -1736,7 +1738,7 @@ impl Engine {
|
|||||||
for item in x.as_ref() {
|
for item in x.as_ref() {
|
||||||
arr.push(self.eval_expr(scope, mods, state, lib, this_ptr, item, level)?);
|
arr.push(self.eval_expr(scope, mods, state, lib, this_ptr, item, level)?);
|
||||||
}
|
}
|
||||||
Ok(Dynamic(Union::Array(Box::new(arr))))
|
Ok(Dynamic(Union::Array(Box::new(arr), AccessMode::ReadWrite)))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
@ -1749,7 +1751,7 @@ impl Engine {
|
|||||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
|
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(Dynamic(Union::Map(Box::new(map))))
|
Ok(Dynamic(Union::Map(Box::new(map), AccessMode::ReadWrite)))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal function call
|
// Normal function call
|
||||||
@ -1766,9 +1768,8 @@ impl Engine {
|
|||||||
let def_value = def_value.as_ref();
|
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, *pos, *cap_scope, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Namespace-qualified function call
|
// Namespace-qualified function call
|
||||||
@ -1785,9 +1786,8 @@ impl Engine {
|
|||||||
let def_value = def_value.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,
|
*pos, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*pos))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::In(x, _) => {
|
Expr::In(x, _) => {
|
||||||
@ -1913,7 +1913,7 @@ impl Engine {
|
|||||||
let mut rhs_val = self
|
let mut rhs_val = self
|
||||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||||
.flatten();
|
.flatten();
|
||||||
let (mut lhs_ptr, name, typ, pos) =
|
let (mut lhs_ptr, name, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, lhs_expr)?;
|
||||||
|
|
||||||
if !lhs_ptr.is_ref() {
|
if !lhs_ptr.is_ref() {
|
||||||
@ -1923,88 +1923,84 @@ impl Engine {
|
|||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(pos))?;
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
|
|
||||||
match typ {
|
if lhs_ptr.as_ref().is_read_only() {
|
||||||
// Assignment to constant variable
|
// Assignment to constant variable
|
||||||
ScopeEntryType::Constant => Err(Box::new(
|
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||||
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos),
|
name.to_string(),
|
||||||
)),
|
pos,
|
||||||
|
)))
|
||||||
|
} else if op.is_empty() {
|
||||||
// Normal assignment
|
// Normal assignment
|
||||||
ScopeEntryType::Normal if op.is_empty() => {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
*lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap() = rhs_val;
|
||||||
*lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap() = rhs_val;
|
} else {
|
||||||
} else {
|
*lhs_ptr.as_mut() = rhs_val;
|
||||||
*lhs_ptr.as_mut() = rhs_val;
|
|
||||||
}
|
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
}
|
}
|
||||||
|
Ok(Dynamic::UNIT)
|
||||||
|
} else {
|
||||||
// Op-assignment - in order of precedence:
|
// Op-assignment - in order of precedence:
|
||||||
ScopeEntryType::Normal => {
|
// 1) Native registered overriding function
|
||||||
// 1) Native registered overriding function
|
// 2) Built-in implementation
|
||||||
// 2) Built-in implementation
|
// 3) Map to `var = var op rhs`
|
||||||
// 3) Map to `var = var op rhs`
|
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||||
let arg_types =
|
let arg_types = once(lhs_ptr.as_mut().type_id()).chain(once(rhs_val.type_id()));
|
||||||
once(lhs_ptr.as_mut().type_id()).chain(once(rhs_val.type_id()));
|
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_namespace
|
.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))
|
||||||
{
|
{
|
||||||
// op= function registered as method
|
// op= function registered as method
|
||||||
Some(func) if func.is_method() => {
|
Some(func) if func.is_method() => {
|
||||||
let mut lock_guard;
|
let mut lock_guard;
|
||||||
let lhs_ptr_inner;
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
lock_guard = lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap();
|
lock_guard = lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap();
|
||||||
lhs_ptr_inner = lock_guard.deref_mut();
|
lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
} else {
|
} else {
|
||||||
lhs_ptr_inner = lhs_ptr.as_mut();
|
lhs_ptr_inner = lhs_ptr.as_mut();
|
||||||
}
|
|
||||||
|
|
||||||
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
|
||||||
|
|
||||||
// Overriding exact implementation
|
|
||||||
if func.is_plugin_fn() {
|
|
||||||
func.get_plugin_fn()
|
|
||||||
.call((self, &*mods, lib).into(), args)?;
|
|
||||||
} else {
|
|
||||||
func.get_native_fn()((self, &*mods, lib).into(), args)?;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// Built-in op-assignment function
|
|
||||||
_ if run_builtin_op_assignment(op, lhs_ptr.as_mut(), &rhs_val)?
|
|
||||||
.is_some() => {}
|
|
||||||
// Not built-in: expand to `var = var op rhs`
|
|
||||||
_ => {
|
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
|
||||||
|
|
||||||
// Clone the LHS value
|
let args = &mut [lhs_ptr_inner, &mut rhs_val];
|
||||||
let args = &mut [&mut lhs_ptr.as_mut().clone(), &mut rhs_val];
|
|
||||||
|
|
||||||
// Run function
|
// Overriding exact implementation
|
||||||
let (value, _) = self
|
if func.is_plugin_fn() {
|
||||||
.exec_fn_call(
|
func.get_plugin_fn()
|
||||||
mods, state, lib, op, 0, args, false, false, false, None,
|
.call((self, &*mods, lib).into(), args)?;
|
||||||
None, level,
|
} else {
|
||||||
)
|
func.get_native_fn()((self, &*mods, lib).into(), args)?;
|
||||||
.map_err(|err| err.fill_position(*op_pos))?;
|
}
|
||||||
|
}
|
||||||
let value = value.flatten();
|
// Built-in op-assignment function
|
||||||
|
_ if run_builtin_op_assignment(op, lhs_ptr.as_mut(), &rhs_val)?
|
||||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
.is_some() => {}
|
||||||
*lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap() = value;
|
// Not built-in: expand to `var = var op rhs`
|
||||||
} else {
|
_ => {
|
||||||
*lhs_ptr.as_mut() = value;
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
}
|
|
||||||
|
// Clone the LHS value
|
||||||
|
let args = &mut [&mut lhs_ptr.as_mut().clone(), &mut rhs_val];
|
||||||
|
|
||||||
|
// Run function
|
||||||
|
let (value, _) = self.exec_fn_call(
|
||||||
|
mods, state, lib, op, 0, args, false, false, false, *op_pos, None,
|
||||||
|
None, level,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
let value = value.flatten();
|
||||||
|
|
||||||
|
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||||
|
*lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap() = value;
|
||||||
|
} else {
|
||||||
|
*lhs_ptr.as_mut() = value;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Ok(Dynamic::UNIT)
|
|
||||||
}
|
}
|
||||||
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2027,10 +2023,10 @@ impl Engine {
|
|||||||
|
|
||||||
let result = self
|
let result = self
|
||||||
.exec_fn_call(
|
.exec_fn_call(
|
||||||
mods, state, lib, op, 0, args, false, false, false, None, None, level,
|
mods, state, lib, op, 0, args, false, false, false, *op_pos, None,
|
||||||
|
None, level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)?;
|
||||||
.map_err(|err| err.fill_position(*op_pos))?;
|
|
||||||
|
|
||||||
Some((result, rhs_expr.position()))
|
Some((result, rhs_expr.position()))
|
||||||
};
|
};
|
||||||
@ -2175,7 +2171,7 @@ impl Engine {
|
|||||||
state.scope_level += 1;
|
state.scope_level += 1;
|
||||||
|
|
||||||
for iter_value in func(iter_obj) {
|
for iter_value in func(iter_obj) {
|
||||||
let (loop_var, _) = scope.get_mut(index);
|
let loop_var = scope.get_mut(index);
|
||||||
|
|
||||||
let value = iter_value.flatten();
|
let value = iter_value.flatten();
|
||||||
if cfg!(not(feature = "no_closure")) && loop_var.is_shared() {
|
if cfg!(not(feature = "no_closure")) && loop_var.is_shared() {
|
||||||
@ -2249,8 +2245,10 @@ impl Engine {
|
|||||||
.map(|_| ().into());
|
.map(|_| ().into());
|
||||||
|
|
||||||
if let Some(result_err) = result.as_ref().err() {
|
if let Some(result_err) = result.as_ref().err() {
|
||||||
if let EvalAltResult::ErrorRuntime(Dynamic(Union::Unit(_)), pos) =
|
if let EvalAltResult::ErrorRuntime(
|
||||||
result_err.as_ref()
|
Dynamic(Union::Unit(_, _)),
|
||||||
|
pos,
|
||||||
|
) = result_err.as_ref()
|
||||||
{
|
{
|
||||||
err.set_position(*pos);
|
err.set_position(*pos);
|
||||||
result = Err(Box::new(err));
|
result = Err(Box::new(err));
|
||||||
@ -2293,8 +2291,8 @@ impl Engine {
|
|||||||
// Let/const statement
|
// Let/const statement
|
||||||
Stmt::Let(var_def, expr, export, _) | Stmt::Const(var_def, expr, export, _) => {
|
Stmt::Let(var_def, expr, export, _) | Stmt::Const(var_def, expr, export, _) => {
|
||||||
let entry_type = match stmt {
|
let entry_type = match stmt {
|
||||||
Stmt::Let(_, _, _, _) => ScopeEntryType::Normal,
|
Stmt::Let(_, _, _, _) => AccessMode::ReadWrite,
|
||||||
Stmt::Const(_, _, _, _) => ScopeEntryType::Constant,
|
Stmt::Const(_, _, _, _) => AccessMode::ReadOnly,
|
||||||
_ => unreachable!(),
|
_ => unreachable!(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -2306,9 +2304,9 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
let (var_name, _alias): (Cow<'_, str>, _) = if state.is_global() {
|
||||||
(
|
(
|
||||||
var_def.name.clone().into(),
|
var_def.name.to_string().into(),
|
||||||
if *export {
|
if *export {
|
||||||
Some(var_def.name.to_string())
|
Some(var_def.name.clone())
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
},
|
},
|
||||||
@ -2375,7 +2373,7 @@ impl Engine {
|
|||||||
// Mark scope variables as public
|
// Mark scope variables as public
|
||||||
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
|
if let Some(index) = scope.get_index(name).map(|(i, _)| i) {
|
||||||
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
|
let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name);
|
||||||
scope.add_entry_alias(index, alias.to_string());
|
scope.add_entry_alias(index, alias.clone());
|
||||||
} else {
|
} else {
|
||||||
return EvalAltResult::ErrorVariableNotFound(name.to_string(), *id_pos)
|
return EvalAltResult::ErrorVariableNotFound(name.to_string(), *id_pos)
|
||||||
.into();
|
.into();
|
||||||
@ -2387,16 +2385,13 @@ impl Engine {
|
|||||||
// Share statement
|
// Share statement
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Stmt::Share(x) => {
|
Stmt::Share(x) => {
|
||||||
match scope.get_index(&x.name) {
|
if let Some((index, _)) = scope.get_index(&x.name) {
|
||||||
Some((index, ScopeEntryType::Normal)) => {
|
let val = scope.get_mut(index);
|
||||||
let (val, _) = scope.get_mut(index);
|
|
||||||
|
|
||||||
if !val.is_shared() {
|
if !val.is_shared() {
|
||||||
// Replace the variable with a shared value.
|
// Replace the variable with a shared value.
|
||||||
*val = crate::stdlib::mem::take(val).into_shared();
|
*val = crate::stdlib::mem::take(val).into_shared();
|
||||||
}
|
|
||||||
}
|
}
|
||||||
_ => (),
|
|
||||||
}
|
}
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
@ -2445,18 +2440,18 @@ impl Engine {
|
|||||||
fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
|
fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
|
||||||
match value {
|
match value {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(arr)) => {
|
Dynamic(Union::Array(arr, _)) => {
|
||||||
let mut arrays = 0;
|
let mut arrays = 0;
|
||||||
let mut maps = 0;
|
let mut maps = 0;
|
||||||
|
|
||||||
arr.iter().for_each(|value| match value {
|
arr.iter().for_each(|value| match value {
|
||||||
Dynamic(Union::Array(_)) => {
|
Dynamic(Union::Array(_, _)) => {
|
||||||
let (a, m, _) = calc_size(value);
|
let (a, m, _) = calc_size(value);
|
||||||
arrays += a;
|
arrays += a;
|
||||||
maps += m;
|
maps += m;
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(_)) => {
|
Dynamic(Union::Map(_, _)) => {
|
||||||
let (a, m, _) = calc_size(value);
|
let (a, m, _) = calc_size(value);
|
||||||
arrays += a;
|
arrays += a;
|
||||||
maps += m;
|
maps += m;
|
||||||
@ -2467,18 +2462,18 @@ impl Engine {
|
|||||||
(arrays, maps, 0)
|
(arrays, maps, 0)
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Dynamic(Union::Map(map)) => {
|
Dynamic(Union::Map(map, _)) => {
|
||||||
let mut arrays = 0;
|
let mut arrays = 0;
|
||||||
let mut maps = 0;
|
let mut maps = 0;
|
||||||
|
|
||||||
map.values().for_each(|value| match value {
|
map.values().for_each(|value| match value {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Dynamic(Union::Array(_)) => {
|
Dynamic(Union::Array(_, _)) => {
|
||||||
let (a, m, _) = calc_size(value);
|
let (a, m, _) = calc_size(value);
|
||||||
arrays += a;
|
arrays += a;
|
||||||
maps += m;
|
maps += m;
|
||||||
}
|
}
|
||||||
Dynamic(Union::Map(_)) => {
|
Dynamic(Union::Map(_, _)) => {
|
||||||
let (a, m, _) = calc_size(value);
|
let (a, m, _) = calc_size(value);
|
||||||
arrays += a;
|
arrays += a;
|
||||||
maps += m;
|
maps += m;
|
||||||
@ -2488,7 +2483,7 @@ impl Engine {
|
|||||||
|
|
||||||
(arrays, maps, 0)
|
(arrays, maps, 0)
|
||||||
}
|
}
|
||||||
Dynamic(Union::Str(s)) => (0, 0, s.len()),
|
Dynamic(Union::Str(s, _)) => (0, 0, s.len()),
|
||||||
_ => (0, 0, 0),
|
_ => (0, 0, 0),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2497,13 +2492,13 @@ impl Engine {
|
|||||||
// Simply return all errors
|
// Simply return all errors
|
||||||
Err(_) => return result,
|
Err(_) => return result,
|
||||||
// String with limit
|
// String with limit
|
||||||
Ok(Dynamic(Union::Str(_))) if self.max_string_size() > 0 => (),
|
Ok(Dynamic(Union::Str(_, _))) if self.max_string_size() > 0 => (),
|
||||||
// Array with limit
|
// Array with limit
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Ok(Dynamic(Union::Array(_))) if self.max_array_size() > 0 => (),
|
Ok(Dynamic(Union::Array(_, _))) if self.max_array_size() > 0 => (),
|
||||||
// Map with limit
|
// Map with limit
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Ok(Dynamic(Union::Map(_))) if self.max_map_size() > 0 => (),
|
Ok(Dynamic(Union::Map(_, _))) if self.max_map_size() > 0 => (),
|
||||||
// Everything else is simply returned
|
// Everything else is simply returned
|
||||||
Ok(_) => return result,
|
Ok(_) => return result,
|
||||||
};
|
};
|
||||||
@ -2549,7 +2544,7 @@ impl Engine {
|
|||||||
|
|
||||||
// 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, Position::NONE).into();
|
return EvalAltResult::ErrorTerminated(token, Position::NONE).into();
|
||||||
}
|
}
|
||||||
|
@ -1611,7 +1611,17 @@ impl Engine {
|
|||||||
crate::fn_call::ensure_no_data_race(name, args, false)?;
|
crate::fn_call::ensure_no_data_race(name, args, false)?;
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
Position::NONE,
|
||||||
|
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.
|
||||||
@ -1726,7 +1736,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// let mut engine = Engine::new();
|
/// let mut engine = Engine::new();
|
||||||
///
|
///
|
||||||
/// engine.on_progress(move |&ops| {
|
/// engine.on_progress(move |ops| {
|
||||||
/// if ops > 10000 {
|
/// if ops > 10000 {
|
||||||
/// Some("Over 10,000 operations!".into())
|
/// Some("Over 10,000 operations!".into())
|
||||||
/// } else if ops % 800 == 0 {
|
/// } else if ops % 800 == 0 {
|
||||||
@ -1748,7 +1758,7 @@ impl Engine {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_progress(
|
pub fn on_progress(
|
||||||
&mut self,
|
&mut self,
|
||||||
callback: impl Fn(&u64) -> Option<Dynamic> + SendSync + 'static,
|
callback: impl Fn(u64) -> Option<Dynamic> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.progress = Some(Box::new(callback));
|
self.progress = Some(Box::new(callback));
|
||||||
self
|
self
|
||||||
@ -1798,16 +1808,21 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// // Override action of 'print' function
|
/// // Override action of 'print' function
|
||||||
/// let logger = result.clone();
|
/// let logger = result.clone();
|
||||||
/// engine.on_debug(move |s| logger.write().unwrap().push_str(s));
|
/// engine.on_debug(move |s, pos| logger.write().unwrap().push_str(
|
||||||
|
/// &format!("{:?} > {}", pos, s)
|
||||||
|
/// ));
|
||||||
///
|
///
|
||||||
/// engine.consume(r#"debug("hello");"#)?;
|
/// engine.consume(r#"let x = "hello"; debug(x);"#)?;
|
||||||
///
|
///
|
||||||
/// assert_eq!(*result.read().unwrap(), r#""hello""#);
|
/// assert_eq!(*result.read().unwrap(), r#"1:18 > "hello""#);
|
||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn on_debug(&mut self, callback: impl Fn(&str) + SendSync + 'static) -> &mut Self {
|
pub fn on_debug(
|
||||||
|
&mut self,
|
||||||
|
callback: impl Fn(&str, Position) + SendSync + 'static,
|
||||||
|
) -> &mut Self {
|
||||||
self.debug = Box::new(callback);
|
self.debug = Box::new(callback);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
|
113
src/fn_call.rs
113
src/fn_call.rs
@ -8,7 +8,6 @@ use crate::engine::{
|
|||||||
use crate::fn_native::FnCallArgs;
|
use crate::fn_native::FnCallArgs;
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::scope::EntryType as ScopeEntryType;
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
any::{type_name, TypeId},
|
any::{type_name, TypeId},
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -150,7 +149,6 @@ 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`][Position::None] and must be set afterwards.
|
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -167,6 +165,7 @@ impl Engine {
|
|||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
|
pos: Position,
|
||||||
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)?;
|
||||||
@ -205,20 +204,23 @@ 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(),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
})?)
|
})?)
|
||||||
.into(),
|
.into(),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
KEYWORD_DEBUG => (
|
KEYWORD_DEBUG => (
|
||||||
(self.debug)(result.as_str().map_err(|typ| {
|
(self.debug)(
|
||||||
EvalAltResult::ErrorMismatchOutputType(
|
result.as_str().map_err(|typ| {
|
||||||
self.map_type_name(type_name::<ImmutableString>()).into(),
|
EvalAltResult::ErrorMismatchOutputType(
|
||||||
typ.into(),
|
self.map_type_name(type_name::<ImmutableString>()).into(),
|
||||||
Position::NONE,
|
typ.into(),
|
||||||
)
|
pos,
|
||||||
})?)
|
)
|
||||||
|
})?,
|
||||||
|
pos,
|
||||||
|
)
|
||||||
.into(),
|
.into(),
|
||||||
false,
|
false,
|
||||||
),
|
),
|
||||||
@ -248,7 +250,7 @@ impl Engine {
|
|||||||
prop,
|
prop,
|
||||||
self.map_type_name(args[0].type_name())
|
self.map_type_name(args[0].type_name())
|
||||||
),
|
),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -263,7 +265,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()),
|
||||||
),
|
),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -277,7 +279,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()),
|
||||||
),
|
),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -291,7 +293,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()),
|
||||||
),
|
),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into();
|
.into();
|
||||||
}
|
}
|
||||||
@ -310,13 +312,12 @@ impl Engine {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Call a script-defined function.
|
/// Call a script-defined function.
|
||||||
/// [`Position`] in [`EvalAltResult`] is [`None`][Position::None] and must be set afterwards.
|
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -333,6 +334,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
fn_def: &crate::ast::ScriptFnDef,
|
fn_def: &crate::ast::ScriptFnDef,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
@ -341,7 +343,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(Position::NONE)));
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
|
||||||
}
|
}
|
||||||
|
|
||||||
let orig_scope_level = state.scope_level;
|
let orig_scope_level = state.scope_level;
|
||||||
@ -360,7 +362,7 @@ impl Engine {
|
|||||||
.map(|(name, value)| {
|
.map(|(name, value)| {
|
||||||
let var_name: crate::stdlib::borrow::Cow<'_, str> =
|
let var_name: crate::stdlib::borrow::Cow<'_, str> =
|
||||||
crate::r#unsafe::unsafe_cast_var_name_to_lifetime(name).into();
|
crate::r#unsafe::unsafe_cast_var_name_to_lifetime(name).into();
|
||||||
(var_name, ScopeEntryType::Normal, value)
|
(var_name, value)
|
||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -393,17 +395,14 @@ impl Engine {
|
|||||||
EvalAltResult::ErrorInFunctionCall(
|
EvalAltResult::ErrorInFunctionCall(
|
||||||
format!("{} > {}", fn_def.name, name),
|
format!("{} > {}", fn_def.name, name),
|
||||||
err,
|
err,
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.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, pos).into(),
|
||||||
EvalAltResult::ErrorInFunctionCall(fn_def.name.to_string(), err, Position::NONE)
|
|
||||||
.into()
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
// Remove all local variables
|
// Remove all local variables
|
||||||
@ -457,7 +456,6 @@ 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`][Position::None] and must be set afterwards.
|
|
||||||
///
|
///
|
||||||
/// ## WARNING
|
/// ## WARNING
|
||||||
///
|
///
|
||||||
@ -475,6 +473,7 @@ impl Engine {
|
|||||||
is_ref: bool,
|
is_ref: bool,
|
||||||
_is_method: bool,
|
_is_method: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
|
pos: Position,
|
||||||
_capture_scope: Option<Scope>,
|
_capture_scope: Option<Scope>,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
@ -512,7 +511,7 @@ impl Engine {
|
|||||||
fn_name, fn_name
|
fn_name, fn_name
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
@ -540,15 +539,10 @@ impl Engine {
|
|||||||
if !func.externals.is_empty() {
|
if !func.externals.is_empty() {
|
||||||
captured
|
captured
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.filter(|(name, _, _, _)| func.externals.contains(name.as_ref()))
|
.filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name))
|
||||||
.for_each(|(name, typ, value, _)| {
|
.for_each(|(name, value, _)| {
|
||||||
// Consume the scope values.
|
// Consume the scope values.
|
||||||
match typ {
|
scope.push_dynamic(name, value);
|
||||||
ScopeEntryType::Normal => scope.push(name, value),
|
|
||||||
ScopeEntryType::Constant => {
|
|
||||||
scope.push_constant(name, value)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -564,6 +558,7 @@ impl Engine {
|
|||||||
&mut Some(*first),
|
&mut Some(*first),
|
||||||
func,
|
func,
|
||||||
rest,
|
rest,
|
||||||
|
pos,
|
||||||
_level,
|
_level,
|
||||||
)?
|
)?
|
||||||
} else {
|
} else {
|
||||||
@ -572,8 +567,9 @@ impl Engine {
|
|||||||
let mut backup: ArgBackup = Default::default();
|
let mut backup: ArgBackup = Default::default();
|
||||||
backup.change_first_arg_to_copy(is_ref, args);
|
backup.change_first_arg_to_copy(is_ref, args);
|
||||||
|
|
||||||
let result = self
|
let result = self.call_script_fn(
|
||||||
.call_script_fn(scope, mods, state, lib, &mut None, func, args, _level);
|
scope, mods, state, lib, &mut None, func, args, pos, _level,
|
||||||
|
);
|
||||||
|
|
||||||
// Restore the original reference
|
// Restore the original reference
|
||||||
backup.restore_first_arg(args);
|
backup.restore_first_arg(args);
|
||||||
@ -593,6 +589,7 @@ impl Engine {
|
|||||||
args,
|
args,
|
||||||
is_ref,
|
is_ref,
|
||||||
pub_only,
|
pub_only,
|
||||||
|
pos,
|
||||||
def_val,
|
def_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -600,7 +597,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Normal native function call
|
// Normal native function call
|
||||||
_ => self.call_native_fn(
|
_ => self.call_native_fn(
|
||||||
mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, def_val,
|
mods, state, lib, fn_name, hash_fn, args, is_ref, pub_only, pos, def_val,
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -631,7 +628,6 @@ 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`][Position::None] and must be set afterwards.
|
|
||||||
fn eval_script_expr(
|
fn eval_script_expr(
|
||||||
&self,
|
&self,
|
||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
@ -639,6 +635,7 @@ impl Engine {
|
|||||||
state: &mut State,
|
state: &mut State,
|
||||||
lib: &[&Module],
|
lib: &[&Module],
|
||||||
script: &str,
|
script: &str,
|
||||||
|
pos: Position,
|
||||||
_level: usize,
|
_level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
self.inc_operations(state)?;
|
self.inc_operations(state)?;
|
||||||
@ -652,7 +649,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(Position::NONE)));
|
return Err(Box::new(EvalAltResult::ErrorStackOverflow(pos)));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compile the script text
|
// Compile the script text
|
||||||
@ -678,7 +675,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a dot method.
|
/// Call a dot method.
|
||||||
/// [`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,
|
||||||
@ -692,6 +688,7 @@ impl Engine {
|
|||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
let is_ref = target.is_ref();
|
let is_ref = target.is_ref();
|
||||||
@ -722,7 +719,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, false, false, pub_only, None, def_val, level,
|
mods, state, lib, fn_name, hash, args, false, false, pub_only, pos, None, def_val,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
} else if fn_name == KEYWORD_FN_PTR_CALL
|
} else if fn_name == KEYWORD_FN_PTR_CALL
|
||||||
&& call_args.len() > 0
|
&& call_args.len() > 0
|
||||||
@ -749,7 +747,8 @@ impl Engine {
|
|||||||
|
|
||||||
// Map it to name(args) in function-call style
|
// Map it to name(args) in function-call style
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
|
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, def_val,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
} else if fn_name == KEYWORD_FN_PTR_CURRY && obj.is::<FnPtr>() {
|
||||||
// Curry call
|
// Curry call
|
||||||
@ -817,7 +816,8 @@ impl Engine {
|
|||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, None, def_val, level,
|
mods, state, lib, fn_name, hash, args, is_ref, true, pub_only, pos, None, def_val,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
}?;
|
}?;
|
||||||
|
|
||||||
@ -830,7 +830,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// Call a function in normal function-call style.
|
/// Call a function in normal function-call style.
|
||||||
/// [`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,
|
||||||
@ -844,6 +843,7 @@ impl Engine {
|
|||||||
mut hash_script: u64,
|
mut hash_script: u64,
|
||||||
native: bool,
|
native: bool,
|
||||||
pub_only: bool,
|
pub_only: bool,
|
||||||
|
pos: Position,
|
||||||
capture_scope: bool,
|
capture_scope: bool,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
@ -962,9 +962,8 @@ impl Engine {
|
|||||||
let script = script.as_str().map_err(|typ| {
|
let script = script.as_str().map_err(|typ| {
|
||||||
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
self.make_type_mismatch_err::<ImmutableString>(typ, args_expr[0].position())
|
||||||
})?;
|
})?;
|
||||||
let result = self
|
let pos = args_expr[0].position();
|
||||||
.eval_script_expr(scope, mods, state, lib, script, level + 1)
|
let result = self.eval_script_expr(scope, mods, state, lib, script, pos, level + 1);
|
||||||
.map_err(|err| err.fill_position(args_expr[0].position()));
|
|
||||||
|
|
||||||
// IMPORTANT! If the eval defines new variables in the current scope,
|
// IMPORTANT! If the eval defines new variables in the current scope,
|
||||||
// all variable offsets from this point on will be mis-aligned.
|
// all variable offsets from this point on will be mis-aligned.
|
||||||
@ -1000,13 +999,12 @@ impl Engine {
|
|||||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
let (target, _, typ, pos) =
|
let (mut target, _, pos) =
|
||||||
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
let target = match typ {
|
if target.as_ref().is_read_only() {
|
||||||
ScopeEntryType::Normal => target,
|
target = target.into_owned();
|
||||||
ScopeEntryType::Constant => target.into_owned(),
|
}
|
||||||
};
|
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
.map_err(|err| err.fill_position(pos))?;
|
.map_err(|err| err.fill_position(pos))?;
|
||||||
@ -1036,13 +1034,13 @@ impl Engine {
|
|||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, name, hash, args, is_ref, false, pub_only, capture, def_val, level,
|
mods, state, lib, name, hash, args, is_ref, false, pub_only, pos, capture, def_val,
|
||||||
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v)
|
.map(|(v, _)| v)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// 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`][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,
|
||||||
@ -1055,6 +1053,7 @@ impl Engine {
|
|||||||
args_expr: impl AsRef<[Expr]>,
|
args_expr: impl AsRef<[Expr]>,
|
||||||
def_val: Option<&Dynamic>,
|
def_val: Option<&Dynamic>,
|
||||||
hash_script: u64,
|
hash_script: u64,
|
||||||
|
pos: Position,
|
||||||
level: usize,
|
level: usize,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let args_expr = args_expr.as_ref();
|
let args_expr = args_expr.as_ref();
|
||||||
@ -1087,7 +1086,7 @@ impl Engine {
|
|||||||
.collect::<Result<_, _>>()?;
|
.collect::<Result<_, _>>()?;
|
||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
let (target, _, _, pos) =
|
let (target, _, pos) =
|
||||||
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
self.search_scope_only(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||||
|
|
||||||
self.inc_operations(state)
|
self.inc_operations(state)
|
||||||
@ -1150,7 +1149,9 @@ impl Engine {
|
|||||||
let args = args.as_mut();
|
let args = args.as_mut();
|
||||||
let new_scope = &mut Default::default();
|
let new_scope = &mut Default::default();
|
||||||
let fn_def = f.get_fn_def().clone();
|
let fn_def = f.get_fn_def().clone();
|
||||||
self.call_script_fn(new_scope, mods, state, lib, &mut None, &fn_def, args, level)
|
self.call_script_fn(
|
||||||
|
new_scope, mods, state, lib, &mut None, &fn_def, args, pos, level,
|
||||||
|
)
|
||||||
}
|
}
|
||||||
Some(f) if f.is_plugin_fn() => f
|
Some(f) if f.is_plugin_fn() => f
|
||||||
.get_plugin_fn()
|
.get_plugin_fn()
|
||||||
@ -1184,7 +1185,7 @@ impl Engine {
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
Position::NONE,
|
pos,
|
||||||
)
|
)
|
||||||
.into(),
|
.into(),
|
||||||
}
|
}
|
||||||
|
@ -164,6 +164,7 @@ impl<'e, 'a, 'm, 'pm> NativeCallContext<'e, 'a, 'm, 'pm> {
|
|||||||
is_method,
|
is_method,
|
||||||
is_method,
|
is_method,
|
||||||
public_only,
|
public_only,
|
||||||
|
Position::NONE,
|
||||||
None,
|
None,
|
||||||
def_value,
|
def_value,
|
||||||
0,
|
0,
|
||||||
@ -343,18 +344,32 @@ pub type FnPlugin = dyn PluginFunction;
|
|||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnPlugin = dyn PluginFunction + Send + Sync;
|
pub type FnPlugin = dyn PluginFunction + Send + Sync;
|
||||||
|
|
||||||
/// A standard callback function.
|
/// A standard callback function for progress reporting.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type Callback<T, R> = Box<dyn Fn(&T) -> R + 'static>;
|
pub type OnProgressCallback = Box<dyn Fn(u64) -> Option<Dynamic> + 'static>;
|
||||||
/// A standard callback function.
|
/// A standard callback function for progress reporting.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type Callback<T, R> = Box<dyn Fn(&T) -> R + Send + Sync + 'static>;
|
pub type OnProgressCallback = Box<dyn Fn(u64) -> Option<Dynamic> + Send + Sync + 'static>;
|
||||||
|
|
||||||
/// A standard callback function.
|
/// A standard callback function for printing.
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
pub type OnPrintCallback = Box<dyn Fn(&str) + 'static>;
|
||||||
|
/// A standard callback function for printing.
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
pub type OnPrintCallback = Box<dyn Fn(&str) + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
/// A standard callback function for debugging.
|
||||||
|
#[cfg(not(feature = "sync"))]
|
||||||
|
pub type OnDebugCallback = Box<dyn Fn(&str, Position) + 'static>;
|
||||||
|
/// A standard callback function for debugging.
|
||||||
|
#[cfg(feature = "sync")]
|
||||||
|
pub type OnDebugCallback = Box<dyn Fn(&str, Position) + Send + Sync + 'static>;
|
||||||
|
|
||||||
|
/// A standard callback function for variable access.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type OnVarCallback =
|
pub type OnVarCallback =
|
||||||
Box<dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static>;
|
Box<dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>> + 'static>;
|
||||||
/// A standard callback function.
|
/// A standard callback function for variable access.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type OnVarCallback = Box<
|
pub type OnVarCallback = Box<
|
||||||
dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
|
dyn Fn(&str, usize, &EvalContext) -> Result<Option<Dynamic>, Box<EvalAltResult>>
|
||||||
|
@ -115,7 +115,7 @@ pub type FLOAT = f64;
|
|||||||
#[cfg(feature = "f32_float")]
|
#[cfg(feature = "f32_float")]
|
||||||
pub type FLOAT = f32;
|
pub type FLOAT = f32;
|
||||||
|
|
||||||
pub use ast::{FnAccess, AST};
|
pub use ast::{FnAccess, ScriptFnMetadata, AST};
|
||||||
pub use dynamic::Dynamic;
|
pub use dynamic::Dynamic;
|
||||||
pub use engine::{Engine, EvalContext};
|
pub use engine::{Engine, EvalContext};
|
||||||
pub use fn_native::{FnPtr, NativeCallContext, Shared};
|
pub use fn_native::{FnPtr, NativeCallContext, Shared};
|
||||||
|
@ -120,9 +120,9 @@ impl FuncInfo {
|
|||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub struct Module {
|
pub struct Module {
|
||||||
/// Sub-modules.
|
/// Sub-modules.
|
||||||
modules: HashMap<String, Shared<Module>>,
|
modules: HashMap<ImmutableString, Shared<Module>>,
|
||||||
/// Module variables.
|
/// Module variables.
|
||||||
variables: HashMap<String, Dynamic>,
|
variables: HashMap<ImmutableString, Dynamic>,
|
||||||
/// Flattened collection of all module variables, including those in sub-modules.
|
/// Flattened collection of all module variables, including those in sub-modules.
|
||||||
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
|
all_variables: HashMap<u64, Dynamic, StraightHasherBuilder>,
|
||||||
/// External Rust functions.
|
/// External Rust functions.
|
||||||
@ -160,7 +160,7 @@ impl fmt::Debug for Module {
|
|||||||
"Module(\n modules: {}\n vars: {}\n functions: {}\n)",
|
"Module(\n modules: {}\n vars: {}\n functions: {}\n)",
|
||||||
self.modules
|
self.modules
|
||||||
.keys()
|
.keys()
|
||||||
.map(String::as_str)
|
.map(|m| m.as_str())
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", "),
|
.join(", "),
|
||||||
self.variables
|
self.variables
|
||||||
@ -331,7 +331,11 @@ impl Module {
|
|||||||
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
/// assert_eq!(module.get_var_value::<i64>("answer").unwrap(), 42);
|
||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_var(&mut self, name: impl Into<String>, value: impl Variant + Clone) -> &mut Self {
|
pub fn set_var(
|
||||||
|
&mut self,
|
||||||
|
name: impl Into<ImmutableString>,
|
||||||
|
value: impl Variant + Clone,
|
||||||
|
) -> &mut Self {
|
||||||
self.variables.insert(name.into(), Dynamic::from(value));
|
self.variables.insert(name.into(), Dynamic::from(value));
|
||||||
self.indexed = false;
|
self.indexed = false;
|
||||||
self
|
self
|
||||||
@ -458,7 +462,7 @@ impl Module {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_sub_module(
|
pub fn set_sub_module(
|
||||||
&mut self,
|
&mut self,
|
||||||
name: impl Into<String>,
|
name: impl Into<ImmutableString>,
|
||||||
sub_module: impl Into<Shared<Module>>,
|
sub_module: impl Into<Shared<Module>>,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.modules.insert(name.into(), sub_module.into());
|
self.modules.insert(name.into(), sub_module.into());
|
||||||
@ -1423,7 +1427,7 @@ impl Module {
|
|||||||
other.modules.iter().for_each(|(k, v)| {
|
other.modules.iter().for_each(|(k, v)| {
|
||||||
let mut m = Self::new();
|
let mut m = Self::new();
|
||||||
m.merge_filtered(v, _filter);
|
m.merge_filtered(v, _filter);
|
||||||
self.set_sub_module(k, m);
|
self.set_sub_module(k.clone(), m);
|
||||||
});
|
});
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
self.modules
|
self.modules
|
||||||
@ -1640,7 +1644,7 @@ impl Module {
|
|||||||
// Create new module
|
// Create new module
|
||||||
let mut module = Module::new();
|
let mut module = Module::new();
|
||||||
|
|
||||||
scope.into_iter().for_each(|(_, _, value, mut aliases)| {
|
scope.into_iter().for_each(|(_, value, mut aliases)| {
|
||||||
// Variables with an alias left in the scope become module variables
|
// Variables with an alias left in the scope become module variables
|
||||||
if aliases.len() > 1 {
|
if aliases.len() > 1 {
|
||||||
aliases.into_iter().for_each(|alias| {
|
aliases.into_iter().for_each(|alias| {
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
//! Module implementing the [`AST`] optimizer.
|
//! Module implementing the [`AST`] optimizer.
|
||||||
|
|
||||||
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
use crate::ast::{Expr, ScriptFnDef, Stmt};
|
||||||
|
use crate::dynamic::AccessMode;
|
||||||
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_PRINT, KEYWORD_TYPE_OF};
|
||||||
use crate::fn_call::run_builtin_binary_op;
|
use crate::fn_call::run_builtin_binary_op;
|
||||||
use crate::parser::map_dynamic_to_expr;
|
use crate::parser::map_dynamic_to_expr;
|
||||||
@ -58,7 +59,7 @@ struct State<'a> {
|
|||||||
/// Has the [`AST`] been changed during this pass?
|
/// Has the [`AST`] been changed during this pass?
|
||||||
changed: bool,
|
changed: bool,
|
||||||
/// Collection of constants to use for eager function evaluations.
|
/// Collection of constants to use for eager function evaluations.
|
||||||
constants: Vec<(String, Expr)>,
|
variables: Vec<(String, AccessMode, Expr)>,
|
||||||
/// An [`Engine`] instance for eager function evaluation.
|
/// An [`Engine`] instance for eager function evaluation.
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
/// [Module] containing script-defined functions.
|
/// [Module] containing script-defined functions.
|
||||||
@ -73,7 +74,7 @@ impl<'a> State<'a> {
|
|||||||
pub fn new(engine: &'a Engine, lib: &'a [&'a Module], level: OptimizationLevel) -> Self {
|
pub fn new(engine: &'a Engine, lib: &'a [&'a Module], level: OptimizationLevel) -> Self {
|
||||||
Self {
|
Self {
|
||||||
changed: false,
|
changed: false,
|
||||||
constants: vec![],
|
variables: vec![],
|
||||||
engine,
|
engine,
|
||||||
lib,
|
lib,
|
||||||
optimization_level: level,
|
optimization_level: level,
|
||||||
@ -94,27 +95,26 @@ impl<'a> State<'a> {
|
|||||||
pub fn is_dirty(&self) -> bool {
|
pub fn is_dirty(&self) -> bool {
|
||||||
self.changed
|
self.changed
|
||||||
}
|
}
|
||||||
/// Does a constant exist?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn contains_constant(&self, name: &str) -> bool {
|
|
||||||
self.constants.iter().any(|(n, _)| n == name)
|
|
||||||
}
|
|
||||||
/// Prune the list of constants back to a specified size.
|
/// Prune the list of constants back to a specified size.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn restore_constants(&mut self, len: usize) {
|
pub fn restore_var(&mut self, len: usize) {
|
||||||
self.constants.truncate(len)
|
self.variables.truncate(len)
|
||||||
}
|
}
|
||||||
/// Add a new constant to the list.
|
/// Add a new constant to the list.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn push_constant(&mut self, name: &str, value: Expr) {
|
pub fn push_var(&mut self, name: &str, access: AccessMode, value: Expr) {
|
||||||
self.constants.push((name.into(), value))
|
self.variables.push((name.into(), access, value))
|
||||||
}
|
}
|
||||||
/// Look up a constant from the list.
|
/// Look up a constant from the list.
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn find_constant(&self, name: &str) -> Option<&Expr> {
|
pub fn find_constant(&self, name: &str) -> Option<&Expr> {
|
||||||
for (n, expr) in self.constants.iter().rev() {
|
for (n, access, expr) in self.variables.iter().rev() {
|
||||||
if n == name {
|
if n == name {
|
||||||
return Some(expr);
|
return if access.is_read_only() {
|
||||||
|
Some(expr)
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -142,6 +142,7 @@ fn call_fn_with_constant_arguments(
|
|||||||
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
arg_values.iter_mut().collect::<StaticVec<_>>().as_mut(),
|
||||||
false,
|
false,
|
||||||
true,
|
true,
|
||||||
|
Position::NONE,
|
||||||
None,
|
None,
|
||||||
)
|
)
|
||||||
.ok()
|
.ok()
|
||||||
@ -157,20 +158,24 @@ fn optimize_stmt_block(
|
|||||||
count_promote_as_dirty: bool,
|
count_promote_as_dirty: bool,
|
||||||
) -> Stmt {
|
) -> Stmt {
|
||||||
let orig_len = statements.len(); // Original number of statements in the block, for change detection
|
let orig_len = statements.len(); // Original number of statements in the block, for change detection
|
||||||
let orig_constants_len = state.constants.len(); // Original number of constants in the state, for restore later
|
let orig_constants_len = state.variables.len(); // Original number of constants in the state, for restore later
|
||||||
|
|
||||||
// Optimize each statement in the block
|
// Optimize each statement in the block
|
||||||
statements.iter_mut().for_each(|stmt| match stmt {
|
statements.iter_mut().for_each(|stmt| match stmt {
|
||||||
// Add constant literals into the state
|
// Add constant literals into the state
|
||||||
Stmt::Const(var_def, Some(expr), _, pos) if expr.is_constant() => {
|
Stmt::Const(var_def, Some(expr), _, _) if expr.is_constant() => {
|
||||||
state.set_dirty();
|
state.push_var(&var_def.name, AccessMode::ReadOnly, mem::take(expr));
|
||||||
state.push_constant(&var_def.name, mem::take(expr));
|
|
||||||
*stmt = Stmt::Noop(*pos); // No need to keep constants
|
|
||||||
}
|
}
|
||||||
Stmt::Const(var_def, None, _, pos) => {
|
Stmt::Const(var_def, None, _, _) => {
|
||||||
state.set_dirty();
|
state.push_var(&var_def.name, AccessMode::ReadOnly, Expr::Unit(var_def.pos));
|
||||||
state.push_constant(&var_def.name, Expr::Unit(var_def.pos));
|
}
|
||||||
*stmt = Stmt::Noop(*pos); // No need to keep constants
|
// Add variables into the state
|
||||||
|
Stmt::Let(var_def, _, _, _) => {
|
||||||
|
state.push_var(
|
||||||
|
&var_def.name,
|
||||||
|
AccessMode::ReadWrite,
|
||||||
|
Expr::Unit(var_def.pos),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
// Optimize the statement
|
// Optimize the statement
|
||||||
_ => optimize_stmt(stmt, state, preserve_result),
|
_ => optimize_stmt(stmt, state, preserve_result),
|
||||||
@ -196,7 +201,9 @@ fn optimize_stmt_block(
|
|||||||
|
|
||||||
while let Some(expr) = statements.pop() {
|
while let Some(expr) = statements.pop() {
|
||||||
match expr {
|
match expr {
|
||||||
Stmt::Let(_, expr, _, _) => removed = expr.as_ref().map(Expr::is_pure).unwrap_or(true),
|
Stmt::Let(_, expr, _, _) | Stmt::Const(_, expr, _, _) => {
|
||||||
|
removed = expr.as_ref().map(Expr::is_pure).unwrap_or(true)
|
||||||
|
}
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
Stmt::Import(expr, _, _) => removed = expr.is_pure(),
|
||||||
_ => {
|
_ => {
|
||||||
@ -241,7 +248,7 @@ fn optimize_stmt_block(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Pop the stack and remove all the local constants
|
// Pop the stack and remove all the local constants
|
||||||
state.restore_constants(orig_constants_len);
|
state.restore_var(orig_constants_len);
|
||||||
|
|
||||||
match &statements[..] {
|
match &statements[..] {
|
||||||
// No statements in block - change to No-op
|
// No statements in block - change to No-op
|
||||||
@ -251,6 +258,8 @@ fn optimize_stmt_block(
|
|||||||
}
|
}
|
||||||
// Only one let statement - leave it alone
|
// Only one let statement - leave it alone
|
||||||
[x] if matches!(x, Stmt::Let(_, _, _, _)) => Stmt::Block(statements, pos),
|
[x] if matches!(x, Stmt::Let(_, _, _, _)) => Stmt::Block(statements, pos),
|
||||||
|
// Only one const statement - leave it alone
|
||||||
|
[x] if matches!(x, Stmt::Const(_, _, _, _)) => Stmt::Block(statements, pos),
|
||||||
// Only one import statement - leave it alone
|
// Only one import statement - leave it alone
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
[x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(statements, pos),
|
[x] if matches!(x, Stmt::Import(_, _, _)) => Stmt::Block(statements, pos),
|
||||||
@ -708,7 +717,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
Expr::FnCall(x, _) => x.args.iter_mut().for_each(|a| optimize_expr(a, state)),
|
Expr::FnCall(x, _) => x.args.iter_mut().for_each(|a| optimize_expr(a, state)),
|
||||||
|
|
||||||
// constant-name
|
// constant-name
|
||||||
Expr::Variable(x) if x.1.is_none() && state.contains_constant(&x.3.name) => {
|
Expr::Variable(x) if x.1.is_none() && state.find_constant(&x.3.name).is_some() => {
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
|
||||||
// Replace constant with value
|
// Replace constant with value
|
||||||
@ -742,22 +751,23 @@ fn optimize_top_level(
|
|||||||
// Set up the state
|
// Set up the state
|
||||||
let mut state = State::new(engine, lib, level);
|
let mut state = State::new(engine, lib, level);
|
||||||
|
|
||||||
// Add constants from the scope that can be made into a literal into the state
|
// Add constants and variables from the scope
|
||||||
scope
|
scope.iter().for_each(|(name, constant, value)| {
|
||||||
.iter()
|
if !constant {
|
||||||
.filter(|(_, typ, _)| *typ)
|
state.push_var(name, AccessMode::ReadWrite, Expr::Unit(Position::NONE));
|
||||||
.for_each(|(name, _, value)| {
|
} else if let Some(val) = map_dynamic_to_expr(value, Position::NONE) {
|
||||||
if let Some(val) = map_dynamic_to_expr(value, Position::NONE) {
|
state.push_var(name, AccessMode::ReadOnly, val);
|
||||||
state.push_constant(name, val);
|
} else {
|
||||||
}
|
state.push_var(name, AccessMode::ReadOnly, Expr::Unit(Position::NONE));
|
||||||
});
|
}
|
||||||
|
});
|
||||||
|
|
||||||
let orig_constants_len = state.constants.len();
|
let orig_constants_len = state.variables.len();
|
||||||
|
|
||||||
// Optimization loop
|
// Optimization loop
|
||||||
loop {
|
loop {
|
||||||
state.reset();
|
state.reset();
|
||||||
state.restore_constants(orig_constants_len);
|
state.restore_var(orig_constants_len);
|
||||||
|
|
||||||
let num_statements = statements.len();
|
let num_statements = statements.len();
|
||||||
|
|
||||||
@ -769,7 +779,7 @@ fn optimize_top_level(
|
|||||||
optimize_expr(value_expr, &mut state);
|
optimize_expr(value_expr, &mut state);
|
||||||
|
|
||||||
if value_expr.is_constant() {
|
if value_expr.is_constant() {
|
||||||
state.push_constant(&var_def.name, value_expr.clone());
|
state.push_var(&var_def.name, AccessMode::ReadOnly, value_expr.clone());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Keep it in the global scope
|
// Keep it in the global scope
|
||||||
@ -779,13 +789,20 @@ fn optimize_top_level(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
Stmt::Const(var_def, None, _, _) => {
|
Stmt::Const(var_def, None, _, _) => {
|
||||||
state.push_constant(&var_def.name, Expr::Unit(var_def.pos));
|
state.push_var(&var_def.name, AccessMode::ReadOnly, Expr::Unit(var_def.pos));
|
||||||
|
}
|
||||||
|
Stmt::Let(var_def, _, _, _) => {
|
||||||
|
state.push_var(
|
||||||
|
&var_def.name,
|
||||||
|
AccessMode::ReadWrite,
|
||||||
|
Expr::Unit(var_def.pos),
|
||||||
|
);
|
||||||
}
|
}
|
||||||
_ => {
|
_ => {
|
||||||
// Keep all variable declarations at this level
|
// Keep all variable declarations at this level
|
||||||
// and always keep the last return value
|
// and always keep the last return value
|
||||||
let keep = match stmt {
|
let keep = match stmt {
|
||||||
Stmt::Let(_, _, _, _) => true,
|
Stmt::Let(_, _, _, _) | Stmt::Const(_, _, _, _) => true,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
Stmt::Import(_, _, _) => true,
|
Stmt::Import(_, _, _) => true,
|
||||||
_ => i == num_statements - 1,
|
_ => i == num_statements - 1,
|
||||||
@ -851,6 +868,7 @@ pub fn optimize_into_ast(
|
|||||||
lib: None,
|
lib: None,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
mods: Default::default(),
|
mods: Default::default(),
|
||||||
|
fn_comments: Default::default(),
|
||||||
})
|
})
|
||||||
.for_each(|fn_def| {
|
.for_each(|fn_def| {
|
||||||
lib2.set_script_fn(fn_def);
|
lib2.set_script_fn(fn_def);
|
||||||
|
@ -172,11 +172,12 @@ mod print_debug_functions {
|
|||||||
let len = map.len();
|
let len = map.len();
|
||||||
|
|
||||||
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
map.iter_mut().enumerate().for_each(|(i, (k, v))| {
|
||||||
result.push_str(&format!("{:?}: ", k));
|
result.push_str(&format!(
|
||||||
result.push_str(&print_with_func(FUNC_TO_DEBUG, &ctx, v));
|
"{:?}: {}{}",
|
||||||
if i < len - 1 {
|
k,
|
||||||
result.push_str(", ");
|
&print_with_func(FUNC_TO_DEBUG, &ctx, v),
|
||||||
}
|
if i < len - 1 { ", " } else { "" }
|
||||||
|
));
|
||||||
});
|
});
|
||||||
|
|
||||||
result.push_str("}");
|
result.push_str("}");
|
||||||
|
@ -121,6 +121,10 @@ pub enum ParseErrorType {
|
|||||||
Reserved(String),
|
Reserved(String),
|
||||||
/// Missing an expression. Wrapped value is the expression type.
|
/// Missing an expression. Wrapped value is the expression type.
|
||||||
ExprExpected(String),
|
ExprExpected(String),
|
||||||
|
/// Defining a doc-comment in an appropriate place (e.g. not at global level).
|
||||||
|
///
|
||||||
|
/// Never appears under the `no_function` feature.
|
||||||
|
WrongDocComment,
|
||||||
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
/// Defining a function `fn` in an appropriate place (e.g. inside another function).
|
||||||
///
|
///
|
||||||
/// Never appears under the `no_function` feature.
|
/// Never appears under the `no_function` feature.
|
||||||
@ -189,6 +193,7 @@ impl ParseErrorType {
|
|||||||
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
Self::FnMissingParams(_) => "Expecting parameters in function declaration",
|
||||||
Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
Self::FnDuplicatedParam(_,_) => "Duplicated parameters in function declaration",
|
||||||
Self::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
Self::FnMissingBody(_) => "Expecting body statement block for function declaration",
|
||||||
|
Self::WrongDocComment => "Doc-comment must be followed immediately by a function definition",
|
||||||
Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
Self::WrongFnDefinition => "Function definitions must be at global level and cannot be inside a block or another function",
|
||||||
Self::WrongExport => "Export statement can only appear at global level",
|
Self::WrongExport => "Export statement can only appear at global level",
|
||||||
Self::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
Self::AssignmentToConstant(_) => "Cannot assign to a constant value",
|
||||||
@ -243,7 +248,9 @@ impl fmt::Display for ParseErrorType {
|
|||||||
Self::LiteralTooLarge(typ, max) => {
|
Self::LiteralTooLarge(typ, max) => {
|
||||||
write!(f, "{} exceeds the maximum limit ({})", typ, max)
|
write!(f, "{} exceeds the maximum limit ({})", typ, max)
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
Self::Reserved(s) => write!(f, "'{}' is a reserved keyword", s),
|
||||||
|
|
||||||
_ => f.write_str(self.desc()),
|
_ => f.write_str(self.desc()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
106
src/parser.rs
106
src/parser.rs
@ -3,12 +3,11 @@
|
|||||||
use crate::ast::{
|
use crate::ast::{
|
||||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
|
BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
|
||||||
};
|
};
|
||||||
use crate::dynamic::Union;
|
use crate::dynamic::{AccessMode, Union};
|
||||||
use crate::engine::{KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
use crate::engine::{KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||||
use crate::module::NamespaceRef;
|
use crate::module::NamespaceRef;
|
||||||
use crate::optimize::optimize_into_ast;
|
use crate::optimize::optimize_into_ast;
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::scope::EntryType as ScopeEntryType;
|
|
||||||
use crate::stdlib::{
|
use crate::stdlib::{
|
||||||
borrow::Cow,
|
borrow::Cow,
|
||||||
boxed::Box,
|
boxed::Box,
|
||||||
@ -22,7 +21,7 @@ use crate::stdlib::{
|
|||||||
vec::Vec,
|
vec::Vec,
|
||||||
};
|
};
|
||||||
use crate::syntax::CustomSyntax;
|
use crate::syntax::CustomSyntax;
|
||||||
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
|
use crate::token::{is_doc_comment, 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, ImmutableString, LexError, ParseError, ParseErrorType,
|
calc_script_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType,
|
||||||
@ -49,7 +48,7 @@ struct ParseState<'e> {
|
|||||||
/// Interned strings.
|
/// Interned strings.
|
||||||
strings: HashMap<String, ImmutableString>,
|
strings: HashMap<String, ImmutableString>,
|
||||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||||
stack: Vec<(ImmutableString, ScopeEntryType)>,
|
stack: Vec<(ImmutableString, AccessMode)>,
|
||||||
/// Size of the local variables stack upon entry of the current block scope.
|
/// Size of the local variables stack upon entry of the current block scope.
|
||||||
entry_stack_len: usize,
|
entry_stack_len: usize,
|
||||||
/// 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).
|
||||||
@ -827,7 +826,7 @@ fn parse_switch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut table = HashMap::with_capacity_and_hasher(16, StraightHasherBuilder);
|
let mut table = HashMap::new();
|
||||||
let mut def_stmt = None;
|
let mut def_stmt = None;
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
@ -916,9 +915,12 @@ fn parse_switch(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut final_table = HashMap::with_capacity_and_hasher(table.len(), StraightHasherBuilder);
|
||||||
|
final_table.extend(table.into_iter());
|
||||||
|
|
||||||
Ok(Stmt::Switch(
|
Ok(Stmt::Switch(
|
||||||
item,
|
item,
|
||||||
Box::new((table, def_stmt)),
|
Box::new((final_table, def_stmt)),
|
||||||
settings.pos,
|
settings.pos,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
@ -1291,11 +1293,11 @@ fn make_assignment_stmt<'a>(
|
|||||||
},
|
},
|
||||||
) = x.as_ref();
|
) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
AccessMode::ReadWrite => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||||
}
|
}
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
ScopeEntryType::Constant => {
|
AccessMode::ReadOnly => {
|
||||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
|
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1318,11 +1320,11 @@ fn make_assignment_stmt<'a>(
|
|||||||
},
|
},
|
||||||
) = x.as_ref();
|
) = x.as_ref();
|
||||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||||
ScopeEntryType::Normal => {
|
AccessMode::ReadWrite => {
|
||||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||||
}
|
}
|
||||||
// Constant values cannot be assigned to
|
// Constant values cannot be assigned to
|
||||||
ScopeEntryType::Constant => {
|
AccessMode::ReadOnly => {
|
||||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
|
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1811,7 +1813,7 @@ fn parse_custom_syntax(
|
|||||||
// Variable searches stop at the first empty variable name.
|
// Variable searches stop at the first empty variable name.
|
||||||
state.stack.resize(
|
state.stack.resize(
|
||||||
state.stack.len() + delta as usize,
|
state.stack.len() + delta as usize,
|
||||||
("".into(), ScopeEntryType::Normal),
|
("".into(), AccessMode::ReadWrite),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
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(),
|
||||||
@ -2110,7 +2112,7 @@ fn parse_for(
|
|||||||
|
|
||||||
let loop_var = state.get_interned_string(name.clone());
|
let loop_var = state.get_interned_string(name.clone());
|
||||||
let prev_stack_len = state.stack.len();
|
let prev_stack_len = state.stack.len();
|
||||||
state.stack.push((loop_var, ScopeEntryType::Normal));
|
state.stack.push((loop_var, AccessMode::ReadWrite));
|
||||||
|
|
||||||
settings.is_breakable = true;
|
settings.is_breakable = true;
|
||||||
let body = parse_block(input, state, lib, settings.level_up())?;
|
let body = parse_block(input, state, lib, settings.level_up())?;
|
||||||
@ -2125,7 +2127,7 @@ fn parse_let(
|
|||||||
input: &mut TokenStream,
|
input: &mut TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
var_type: ScopeEntryType,
|
var_type: AccessMode,
|
||||||
export: bool,
|
export: bool,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Stmt, ParseError> {
|
) -> Result<Stmt, ParseError> {
|
||||||
@ -2156,17 +2158,17 @@ fn parse_let(
|
|||||||
|
|
||||||
match var_type {
|
match var_type {
|
||||||
// let name = expr
|
// let name = expr
|
||||||
ScopeEntryType::Normal => {
|
AccessMode::ReadWrite => {
|
||||||
let var_name = state.get_interned_string(name.clone());
|
let var_name = state.get_interned_string(name.clone());
|
||||||
state.stack.push((var_name, ScopeEntryType::Normal));
|
state.stack.push((var_name, AccessMode::ReadWrite));
|
||||||
let var_def = Ident::new(name, pos);
|
let var_def = IdentX::new(name, pos);
|
||||||
Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos))
|
Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos))
|
||||||
}
|
}
|
||||||
// const name = { expr:constant }
|
// const name = { expr:constant }
|
||||||
ScopeEntryType::Constant => {
|
AccessMode::ReadOnly => {
|
||||||
let var_name = state.get_interned_string(name.clone());
|
let var_name = state.get_interned_string(name.clone());
|
||||||
state.stack.push((var_name, ScopeEntryType::Constant));
|
state.stack.push((var_name, AccessMode::ReadOnly));
|
||||||
let var_def = Ident::new(name, pos);
|
let var_def = IdentX::new(name, pos);
|
||||||
Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos))
|
Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2232,13 +2234,13 @@ fn parse_export(
|
|||||||
match input.peek().unwrap() {
|
match input.peek().unwrap() {
|
||||||
(Token::Let, pos) => {
|
(Token::Let, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
let mut stmt = parse_let(input, state, lib, ScopeEntryType::Normal, true, settings)?;
|
let mut stmt = parse_let(input, state, lib, AccessMode::ReadWrite, true, settings)?;
|
||||||
stmt.set_position(pos);
|
stmt.set_position(pos);
|
||||||
return Ok(stmt);
|
return Ok(stmt);
|
||||||
}
|
}
|
||||||
(Token::Const, pos) => {
|
(Token::Const, pos) => {
|
||||||
let pos = *pos;
|
let pos = *pos;
|
||||||
let mut stmt = parse_let(input, state, lib, ScopeEntryType::Constant, true, settings)?;
|
let mut stmt = parse_let(input, state, lib, AccessMode::ReadOnly, true, settings)?;
|
||||||
stmt.set_position(pos);
|
stmt.set_position(pos);
|
||||||
return Ok(stmt);
|
return Ok(stmt);
|
||||||
}
|
}
|
||||||
@ -2393,7 +2395,38 @@ fn parse_stmt(
|
|||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
) -> Result<Option<Stmt>, ParseError> {
|
) -> Result<Option<Stmt>, ParseError> {
|
||||||
use ScopeEntryType::{Constant, Normal};
|
use AccessMode::{ReadOnly, ReadWrite};
|
||||||
|
|
||||||
|
let mut fn_comments: Vec<String> = Default::default();
|
||||||
|
let mut fn_comments_pos = Position::NONE;
|
||||||
|
|
||||||
|
// Handle doc-comment.
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
while let (Token::Comment(ref comment), comment_pos) = input.peek().unwrap() {
|
||||||
|
if fn_comments_pos.is_none() {
|
||||||
|
fn_comments_pos = *comment_pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
if !is_doc_comment(comment) {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
|
||||||
|
if !settings.is_global {
|
||||||
|
return Err(PERR::WrongDocComment.into_err(fn_comments_pos));
|
||||||
|
}
|
||||||
|
|
||||||
|
if let Token::Comment(comment) = input.next().unwrap().0 {
|
||||||
|
fn_comments.push(comment);
|
||||||
|
|
||||||
|
match input.peek().unwrap() {
|
||||||
|
(Token::Fn, _) | (Token::Private, _) => break,
|
||||||
|
(Token::Comment(_), _) => (),
|
||||||
|
_ => return Err(PERR::WrongDocComment.into_err(fn_comments_pos)),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
unreachable!();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let (token, token_pos) = match input.peek().unwrap() {
|
let (token, token_pos) = match input.peek().unwrap() {
|
||||||
(Token::EOF, pos) => return Ok(Some(Stmt::Noop(*pos))),
|
(Token::EOF, pos) => return Ok(Some(Stmt::Noop(*pos))),
|
||||||
@ -2447,7 +2480,7 @@ fn parse_stmt(
|
|||||||
pos: pos,
|
pos: pos,
|
||||||
};
|
};
|
||||||
|
|
||||||
let func = parse_fn(input, &mut new_state, lib, access, settings)?;
|
let func = parse_fn(input, &mut new_state, lib, access, settings, fn_comments)?;
|
||||||
|
|
||||||
// Qualifiers (none) + function name + number of arguments.
|
// Qualifiers (none) + function name + number of arguments.
|
||||||
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len());
|
let hash = calc_script_fn_hash(empty(), &func.name, func.params.len());
|
||||||
@ -2521,9 +2554,9 @@ fn parse_stmt(
|
|||||||
|
|
||||||
Token::Try => parse_try_catch(input, state, lib, settings.level_up()).map(Some),
|
Token::Try => parse_try_catch(input, state, lib, settings.level_up()).map(Some),
|
||||||
|
|
||||||
Token::Let => parse_let(input, state, lib, Normal, false, settings.level_up()).map(Some),
|
Token::Let => parse_let(input, state, lib, ReadWrite, false, settings.level_up()).map(Some),
|
||||||
Token::Const => {
|
Token::Const => {
|
||||||
parse_let(input, state, lib, Constant, false, settings.level_up()).map(Some)
|
parse_let(input, state, lib, ReadOnly, false, settings.level_up()).map(Some)
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -2606,6 +2639,7 @@ fn parse_fn(
|
|||||||
lib: &mut FunctionsLib,
|
lib: &mut FunctionsLib,
|
||||||
access: FnAccess,
|
access: FnAccess,
|
||||||
mut settings: ParseSettings,
|
mut settings: ParseSettings,
|
||||||
|
fn_comments: Vec<String>,
|
||||||
) -> Result<ScriptFnDef, ParseError> {
|
) -> Result<ScriptFnDef, ParseError> {
|
||||||
#[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)?;
|
||||||
@ -2637,7 +2671,7 @@ fn parse_fn(
|
|||||||
return Err(PERR::FnDuplicatedParam(name, s).into_err(pos));
|
return Err(PERR::FnDuplicatedParam(name, s).into_err(pos));
|
||||||
}
|
}
|
||||||
let s = state.get_interned_string(s);
|
let s = state.get_interned_string(s);
|
||||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
state.stack.push((s.clone(), AccessMode::ReadWrite));
|
||||||
params.push((s, pos))
|
params.push((s, pos))
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
@ -2691,6 +2725,7 @@ fn parse_fn(
|
|||||||
lib: None,
|
lib: None,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
mods: Default::default(),
|
mods: Default::default(),
|
||||||
|
fn_comments,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2770,7 +2805,7 @@ fn parse_anon_fn(
|
|||||||
return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos));
|
return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos));
|
||||||
}
|
}
|
||||||
let s = state.get_interned_string(s);
|
let s = state.get_interned_string(s);
|
||||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
state.stack.push((s.clone(), AccessMode::ReadWrite));
|
||||||
params.push((s, pos))
|
params.push((s, pos))
|
||||||
}
|
}
|
||||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||||
@ -2847,6 +2882,7 @@ fn parse_anon_fn(
|
|||||||
lib: None,
|
lib: None,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
mods: Default::default(),
|
mods: Default::default(),
|
||||||
|
fn_comments: Default::default(),
|
||||||
};
|
};
|
||||||
|
|
||||||
let expr = Expr::FnPointer(fn_name, settings.pos);
|
let expr = Expr::FnPointer(fn_name, settings.pos);
|
||||||
@ -3005,15 +3041,15 @@ impl Engine {
|
|||||||
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
||||||
match value.0 {
|
match value.0 {
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(value) => Some(Expr::FloatConstant(value, pos)),
|
Union::Float(value, _) => Some(Expr::FloatConstant(value, pos)),
|
||||||
|
|
||||||
Union::Unit(_) => Some(Expr::Unit(pos)),
|
Union::Unit(_, _) => Some(Expr::Unit(pos)),
|
||||||
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(value) => Some(Expr::BoolConstant(value, pos)),
|
Union::Bool(value, _) => Some(Expr::BoolConstant(value, 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
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|x| map_dynamic_to_expr(x, pos))
|
.map(|x| map_dynamic_to_expr(x, pos))
|
||||||
@ -3029,7 +3065,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(map) => {
|
Union::Map(map, _) => {
|
||||||
let items: Vec<_> = map
|
let items: Vec<_> = map
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.map(|(k, v)| (IdentX::new(k, pos), map_dynamic_to_expr(v, pos)))
|
.map(|(k, v)| (IdentX::new(k, pos), map_dynamic_to_expr(v, pos)))
|
||||||
|
94
src/scope.rs
94
src/scope.rs
@ -1,28 +1,8 @@
|
|||||||
//! 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::{AccessMode, Variant};
|
||||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
use crate::stdlib::{borrow::Cow, boxed::Box, iter, vec::Vec};
|
||||||
use crate::{Dynamic, StaticVec};
|
use crate::{Dynamic, ImmutableString, StaticVec};
|
||||||
|
|
||||||
/// Type of an entry in the Scope.
|
|
||||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
|
||||||
pub enum EntryType {
|
|
||||||
/// Normal value.
|
|
||||||
Normal,
|
|
||||||
/// Immutable constant value.
|
|
||||||
Constant,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl EntryType {
|
|
||||||
/// Is this entry constant?
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_constant(&self) -> bool {
|
|
||||||
match self {
|
|
||||||
Self::Normal => false,
|
|
||||||
Self::Constant => true,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Type containing information about the current scope.
|
/// Type containing information about the current scope.
|
||||||
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
||||||
@ -58,28 +38,25 @@ 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 two [`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>`][Cow] 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)]
|
||||||
pub struct Scope<'a> {
|
pub struct Scope<'a> {
|
||||||
/// Current value of the entry.
|
/// Current value of the entry.
|
||||||
values: Vec<Dynamic>,
|
values: Vec<Dynamic>,
|
||||||
/// Type of the entry.
|
|
||||||
types: Vec<EntryType>,
|
|
||||||
/// (Name, aliases) of the entry. The list of aliases is Boxed because it occurs rarely.
|
/// (Name, aliases) of the entry. The list of aliases is Boxed because it occurs rarely.
|
||||||
names: Vec<(Cow<'a, str>, Box<StaticVec<String>>)>,
|
names: Vec<(Cow<'a, str>, Box<StaticVec<ImmutableString>>)>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Scope<'_> {
|
impl Default for Scope<'_> {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
values: Vec::with_capacity(16),
|
values: Vec::with_capacity(16),
|
||||||
types: Vec::with_capacity(16),
|
|
||||||
names: Vec::with_capacity(16),
|
names: Vec::with_capacity(16),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -124,7 +101,6 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn clear(&mut self) -> &mut Self {
|
pub fn clear(&mut self) -> &mut Self {
|
||||||
self.names.clear();
|
self.names.clear();
|
||||||
self.types.clear();
|
|
||||||
self.values.clear();
|
self.values.clear();
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -180,7 +156,7 @@ impl<'a> Scope<'a> {
|
|||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Cow<'a, str>>,
|
||||||
value: impl Variant + Clone,
|
value: impl Variant + Clone,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
|
self.push_dynamic_value(name, AccessMode::ReadWrite, Dynamic::from(value))
|
||||||
}
|
}
|
||||||
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
||||||
///
|
///
|
||||||
@ -196,7 +172,7 @@ impl<'a> Scope<'a> {
|
|||||||
/// ```
|
/// ```
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
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, value.access_mode(), value)
|
||||||
}
|
}
|
||||||
/// Add (push) a new constant to the [`Scope`].
|
/// Add (push) a new constant to the [`Scope`].
|
||||||
///
|
///
|
||||||
@ -219,7 +195,7 @@ impl<'a> Scope<'a> {
|
|||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Cow<'a, str>>,
|
||||||
value: impl Variant + Clone,
|
value: impl Variant + Clone,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
|
self.push_dynamic_value(name, AccessMode::ReadOnly, 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.
|
||||||
///
|
///
|
||||||
@ -242,18 +218,18 @@ impl<'a> Scope<'a> {
|
|||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Cow<'a, str>>,
|
||||||
value: Dynamic,
|
value: Dynamic,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.push_dynamic_value(name, EntryType::Constant, value)
|
self.push_dynamic_value(name, AccessMode::ReadOnly, 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,
|
||||||
name: impl Into<Cow<'a, str>>,
|
name: impl Into<Cow<'a, str>>,
|
||||||
entry_type: EntryType,
|
access: AccessMode,
|
||||||
value: Dynamic,
|
mut value: Dynamic,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.names.push((name.into(), Box::new(Default::default())));
|
self.names.push((name.into(), Box::new(Default::default())));
|
||||||
self.types.push(entry_type);
|
value.set_access_mode(access);
|
||||||
self.values.push(value.into());
|
self.values.push(value.into());
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -286,7 +262,6 @@ impl<'a> Scope<'a> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||||
self.names.truncate(size);
|
self.names.truncate(size);
|
||||||
self.types.truncate(size);
|
|
||||||
self.values.truncate(size);
|
self.values.truncate(size);
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -312,14 +287,14 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
/// 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, AccessMode)> {
|
||||||
self.names
|
self.names
|
||||||
.iter()
|
.iter()
|
||||||
.enumerate()
|
.enumerate()
|
||||||
.rev() // Always search a Scope in reverse order
|
.rev() // Always search a Scope in reverse order
|
||||||
.find_map(|(index, (key, _))| {
|
.find_map(|(index, (key, _))| {
|
||||||
if name == key.as_ref() {
|
if name == key.as_ref() {
|
||||||
Some((index, self.types[index]))
|
Some((index, self.values[index].access_mode()))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
@ -374,8 +349,8 @@ impl<'a> Scope<'a> {
|
|||||||
None => {
|
None => {
|
||||||
self.push(name, value);
|
self.push(name, value);
|
||||||
}
|
}
|
||||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
Some((_, AccessMode::ReadOnly)) => panic!("variable {} is constant", name),
|
||||||
Some((index, EntryType::Normal)) => {
|
Some((index, AccessMode::ReadWrite)) => {
|
||||||
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -383,19 +358,20 @@ impl<'a> Scope<'a> {
|
|||||||
}
|
}
|
||||||
/// 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 {
|
||||||
(
|
self.values.get_mut(index).expect("invalid index in Scope")
|
||||||
self.values.get_mut(index).expect("invalid index in Scope"),
|
|
||||||
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: impl Into<ImmutableString> + PartialEq<ImmutableString>,
|
||||||
|
) -> &mut Self {
|
||||||
let entry = self.names.get_mut(index).expect("invalid index in Scope");
|
let entry = self.names.get_mut(index).expect("invalid index in Scope");
|
||||||
if !entry.1.contains(&alias) {
|
if !entry.1.iter().any(|a| &alias == a) {
|
||||||
entry.1.push(alias);
|
entry.1.push(alias.into());
|
||||||
}
|
}
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
@ -412,7 +388,6 @@ impl<'a> Scope<'a> {
|
|||||||
.for_each(|(index, (name, alias))| {
|
.for_each(|(index, (name, alias))| {
|
||||||
if !entries.names.iter().any(|(key, _)| key == name) {
|
if !entries.names.iter().any(|(key, _)| key == name) {
|
||||||
entries.names.push((name.clone(), alias.clone()));
|
entries.names.push((name.clone(), alias.clone()));
|
||||||
entries.types.push(self.types[index]);
|
|
||||||
entries.values.push(self.values[index].clone());
|
entries.values.push(self.values[index].clone());
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@ -424,11 +399,11 @@ impl<'a> Scope<'a> {
|
|||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
pub(crate) fn into_iter(
|
pub(crate) fn into_iter(
|
||||||
self,
|
self,
|
||||||
) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Vec<String>)> {
|
) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<ImmutableString>)> {
|
||||||
self.names
|
self.names
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.types.into_iter().zip(self.values.into_iter()))
|
.zip(self.values.into_iter())
|
||||||
.map(|((name, alias), (typ, value))| (name, typ, value, alias.to_vec()))
|
.map(|((name, alias), value)| (name, 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.
|
||||||
@ -466,17 +441,16 @@ impl<'a> Scope<'a> {
|
|||||||
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 {
|
||||||
self.names
|
self.names
|
||||||
.iter()
|
.iter()
|
||||||
.zip(self.types.iter().zip(self.values.iter()))
|
.zip(self.values.iter())
|
||||||
.map(|((name, _), (typ, value))| (name.as_ref(), typ.is_constant(), value))
|
.map(|((name, _), value)| (name.as_ref(), value.is_read_only(), value))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, EntryType, Dynamic)> for Scope<'a> {
|
impl<'a, K: Into<Cow<'a, str>>> iter::Extend<(K, Dynamic)> for Scope<'a> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||||
iter.into_iter().for_each(|(name, typ, value)| {
|
iter.into_iter().for_each(|(name, value)| {
|
||||||
self.names.push((name.into(), Box::new(Default::default())));
|
self.names.push((name.into(), Box::new(Default::default())));
|
||||||
self.types.push(typ);
|
|
||||||
self.values.push(value);
|
self.values.push(value);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -132,39 +132,39 @@ impl<'de> Deserializer<'de> for &mut DynamicDeserializer<'de> {
|
|||||||
|
|
||||||
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
fn deserialize_any<V: Visitor<'de>>(self, visitor: V) -> Result<V::Value, Box<EvalAltResult>> {
|
||||||
match &self.value.0 {
|
match &self.value.0 {
|
||||||
Union::Unit(_) => self.deserialize_unit(visitor),
|
Union::Unit(_, _) => self.deserialize_unit(visitor),
|
||||||
Union::Bool(_) => self.deserialize_bool(visitor),
|
Union::Bool(_, _) => self.deserialize_bool(visitor),
|
||||||
Union::Str(_) => self.deserialize_str(visitor),
|
Union::Str(_, _) => self.deserialize_str(visitor),
|
||||||
Union::Char(_) => self.deserialize_char(visitor),
|
Union::Char(_, _) => self.deserialize_char(visitor),
|
||||||
#[cfg(not(feature = "only_i32"))]
|
#[cfg(not(feature = "only_i32"))]
|
||||||
Union::Int(_) => self.deserialize_i64(visitor),
|
Union::Int(_, _) => self.deserialize_i64(visitor),
|
||||||
#[cfg(feature = "only_i32")]
|
#[cfg(feature = "only_i32")]
|
||||||
Union::Int(_) => self.deserialize_i32(visitor),
|
Union::Int(_, _) => self.deserialize_i32(visitor),
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
Union::Float(_) => self.deserialize_f64(visitor),
|
Union::Float(_, _) => self.deserialize_f64(visitor),
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Union::Array(_) => self.deserialize_seq(visitor),
|
Union::Array(_, _) => self.deserialize_seq(visitor),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => self.deserialize_map(visitor),
|
Union::Map(_, _) => self.deserialize_map(visitor),
|
||||||
Union::FnPtr(_) => self.type_error(),
|
Union::FnPtr(_, _) => self.type_error(),
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
Union::TimeStamp(_) => self.type_error(),
|
Union::TimeStamp(_, _) => self.type_error(),
|
||||||
|
|
||||||
Union::Variant(value) if value.is::<i8>() => self.deserialize_i8(visitor),
|
Union::Variant(value, _) if value.is::<i8>() => self.deserialize_i8(visitor),
|
||||||
Union::Variant(value) if value.is::<i16>() => self.deserialize_i16(visitor),
|
Union::Variant(value, _) if value.is::<i16>() => self.deserialize_i16(visitor),
|
||||||
Union::Variant(value) if value.is::<i32>() => self.deserialize_i32(visitor),
|
Union::Variant(value, _) if value.is::<i32>() => self.deserialize_i32(visitor),
|
||||||
Union::Variant(value) if value.is::<i64>() => self.deserialize_i64(visitor),
|
Union::Variant(value, _) if value.is::<i64>() => self.deserialize_i64(visitor),
|
||||||
Union::Variant(value) if value.is::<i128>() => self.deserialize_i128(visitor),
|
Union::Variant(value, _) if value.is::<i128>() => self.deserialize_i128(visitor),
|
||||||
Union::Variant(value) if value.is::<u8>() => self.deserialize_u8(visitor),
|
Union::Variant(value, _) if value.is::<u8>() => self.deserialize_u8(visitor),
|
||||||
Union::Variant(value) if value.is::<u16>() => self.deserialize_u16(visitor),
|
Union::Variant(value, _) if value.is::<u16>() => self.deserialize_u16(visitor),
|
||||||
Union::Variant(value) if value.is::<u32>() => self.deserialize_u32(visitor),
|
Union::Variant(value, _) if value.is::<u32>() => self.deserialize_u32(visitor),
|
||||||
Union::Variant(value) if value.is::<u64>() => self.deserialize_u64(visitor),
|
Union::Variant(value, _) if value.is::<u64>() => self.deserialize_u64(visitor),
|
||||||
Union::Variant(value) if value.is::<u128>() => self.deserialize_u128(visitor),
|
Union::Variant(value, _) if value.is::<u128>() => self.deserialize_u128(visitor),
|
||||||
|
|
||||||
Union::Variant(_) => self.type_error(),
|
Union::Variant(_, _) => self.type_error(),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
Union::Shared(_) => self.type_error(),
|
Union::Shared(_, _) => self.type_error(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
55
src/token.rs
55
src/token.rs
@ -355,6 +355,9 @@ impl Token {
|
|||||||
Custom(s) => s.clone().into(),
|
Custom(s) => s.clone().into(),
|
||||||
LexError(err) => err.to_string().into(),
|
LexError(err) => err.to_string().into(),
|
||||||
|
|
||||||
|
Comment(s) if is_doc_comment(s) => s[..3].to_string().into(),
|
||||||
|
Comment(s) => s[..2].to_string().into(),
|
||||||
|
|
||||||
token => match token {
|
token => match token {
|
||||||
LeftBrace => "{",
|
LeftBrace => "{",
|
||||||
RightBrace => "}",
|
RightBrace => "}",
|
||||||
@ -917,36 +920,36 @@ fn eat_next(stream: &mut impl InputStream, pos: &mut Position) -> Option<char> {
|
|||||||
/// Scan for a block comment until the end.
|
/// Scan for a block comment until the end.
|
||||||
fn scan_comment(
|
fn scan_comment(
|
||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
state: &mut TokenizeState,
|
mut level: usize,
|
||||||
pos: &mut Position,
|
pos: &mut Position,
|
||||||
comment: &mut String,
|
comment: &mut String,
|
||||||
) {
|
) -> usize {
|
||||||
while let Some(c) = stream.get_next() {
|
while let Some(c) = stream.get_next() {
|
||||||
pos.advance();
|
pos.advance();
|
||||||
|
|
||||||
if state.include_comments {
|
if !comment.is_empty() {
|
||||||
comment.push(c);
|
comment.push(c);
|
||||||
}
|
}
|
||||||
|
|
||||||
match c {
|
match c {
|
||||||
'/' => {
|
'/' => {
|
||||||
if let Some(c2) = stream.get_next() {
|
if let Some(c2) = stream.get_next() {
|
||||||
if state.include_comments {
|
if !comment.is_empty() {
|
||||||
comment.push(c2);
|
comment.push(c2);
|
||||||
}
|
}
|
||||||
if c2 == '*' {
|
if c2 == '*' {
|
||||||
state.comment_level += 1;
|
level += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos.advance();
|
pos.advance();
|
||||||
}
|
}
|
||||||
'*' => {
|
'*' => {
|
||||||
if let Some(c2) = stream.get_next() {
|
if let Some(c2) = stream.get_next() {
|
||||||
if state.include_comments {
|
if !comment.is_empty() {
|
||||||
comment.push(c2);
|
comment.push(c2);
|
||||||
}
|
}
|
||||||
if c2 == '/' {
|
if c2 == '/' {
|
||||||
state.comment_level -= 1;
|
level -= 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
pos.advance();
|
pos.advance();
|
||||||
@ -955,10 +958,12 @@ fn scan_comment(
|
|||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.comment_level == 0 {
|
if level == 0 {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
level
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(INTERNALS)_ Get the next token from the `stream`.
|
/// _(INTERNALS)_ Get the next token from the `stream`.
|
||||||
@ -1012,6 +1017,12 @@ fn is_binary_char(c: char) -> bool {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Test if the comment block is a doc-comment.
|
||||||
|
#[inline(always)]
|
||||||
|
pub fn is_doc_comment(comment: &str) -> bool {
|
||||||
|
comment.starts_with("///") || comment.starts_with("/**")
|
||||||
|
}
|
||||||
|
|
||||||
/// Get the next token.
|
/// Get the next token.
|
||||||
fn get_next_token_inner(
|
fn get_next_token_inner(
|
||||||
stream: &mut impl InputStream,
|
stream: &mut impl InputStream,
|
||||||
@ -1022,9 +1033,9 @@ fn get_next_token_inner(
|
|||||||
if state.comment_level > 0 {
|
if state.comment_level > 0 {
|
||||||
let start_pos = *pos;
|
let start_pos = *pos;
|
||||||
let mut comment = String::new();
|
let mut comment = String::new();
|
||||||
scan_comment(stream, state, pos, &mut comment);
|
state.comment_level = scan_comment(stream, state.comment_level, pos, &mut comment);
|
||||||
|
|
||||||
if state.include_comments {
|
if state.include_comments || is_doc_comment(&comment) {
|
||||||
return Some((Token::Comment(comment), start_pos));
|
return Some((Token::Comment(comment), start_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1271,10 +1282,10 @@ fn get_next_token_inner(
|
|||||||
('/', '/') => {
|
('/', '/') => {
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
|
|
||||||
let mut comment = if state.include_comments {
|
let mut comment = match stream.peek_next().unwrap() {
|
||||||
"//".to_string()
|
'/' => "///".to_string(),
|
||||||
} else {
|
_ if state.include_comments => "//".to_string(),
|
||||||
String::new()
|
_ => String::new(),
|
||||||
};
|
};
|
||||||
|
|
||||||
while let Some(c) = stream.get_next() {
|
while let Some(c) = stream.get_next() {
|
||||||
@ -1283,13 +1294,13 @@ fn get_next_token_inner(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.include_comments {
|
if !comment.is_empty() {
|
||||||
comment.push(c);
|
comment.push(c);
|
||||||
}
|
}
|
||||||
pos.advance();
|
pos.advance();
|
||||||
}
|
}
|
||||||
|
|
||||||
if state.include_comments {
|
if state.include_comments || is_doc_comment(&comment) {
|
||||||
return Some((Token::Comment(comment), start_pos));
|
return Some((Token::Comment(comment), start_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1298,14 +1309,14 @@ fn get_next_token_inner(
|
|||||||
|
|
||||||
eat_next(stream, pos);
|
eat_next(stream, pos);
|
||||||
|
|
||||||
let mut comment = if state.include_comments {
|
let mut comment = match stream.peek_next().unwrap() {
|
||||||
"/*".to_string()
|
'*' => "/**".to_string(),
|
||||||
} else {
|
_ if state.include_comments => "/*".to_string(),
|
||||||
String::new()
|
_ => String::new(),
|
||||||
};
|
};
|
||||||
scan_comment(stream, state, pos, &mut comment);
|
state.comment_level = scan_comment(stream, state.comment_level, pos, &mut comment);
|
||||||
|
|
||||||
if state.include_comments {
|
if state.include_comments || is_doc_comment(&comment) {
|
||||||
return Some((Token::Comment(comment), start_pos));
|
return Some((Token::Comment(comment), start_pos));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -178,7 +178,10 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
|
|||||||
engine.eval::<INT>(
|
engine.eval::<INT>(
|
||||||
r#"
|
r#"
|
||||||
let x = [1, 2, 3];
|
let x = [1, 2, 3];
|
||||||
x.reduce(|sum, v, i| { if i == 0 { sum = 10 } sum + v * v })
|
x.reduce(|sum, v, i| {
|
||||||
|
if i == 0 { sum = 10 }
|
||||||
|
sum + v * v
|
||||||
|
})
|
||||||
"#
|
"#
|
||||||
)?,
|
)?,
|
||||||
24
|
24
|
||||||
|
@ -6,7 +6,7 @@ fn test_max_operations() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|&count| {
|
engine.on_progress(|count| {
|
||||||
if count % 100 == 0 {
|
if count % 100 == 0 {
|
||||||
println!("{}", count);
|
println!("{}", count);
|
||||||
}
|
}
|
||||||
@ -34,7 +34,7 @@ fn test_max_operations_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|&count| {
|
engine.on_progress(|count| {
|
||||||
if count % 100 == 0 {
|
if count % 100 == 0 {
|
||||||
println!("{}", count);
|
println!("{}", count);
|
||||||
}
|
}
|
||||||
@ -90,7 +90,7 @@ fn test_max_operations_eval() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|&count| {
|
engine.on_progress(|count| {
|
||||||
if count % 100 == 0 {
|
if count % 100 == 0 {
|
||||||
println!("{}", count);
|
println!("{}", count);
|
||||||
}
|
}
|
||||||
@ -117,7 +117,7 @@ fn test_max_operations_progress() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_max_operations(500);
|
engine.set_max_operations(500);
|
||||||
|
|
||||||
engine.on_progress(|&count| {
|
engine.on_progress(|count| {
|
||||||
if count < 100 {
|
if count < 100 {
|
||||||
None
|
None
|
||||||
} else {
|
} else {
|
||||||
|
@ -53,15 +53,19 @@ fn test_optimizer_parse() -> Result<(), Box<EvalAltResult>> {
|
|||||||
let mut engine = Engine::new();
|
let mut engine = Engine::new();
|
||||||
engine.set_optimization_level(OptimizationLevel::Simple);
|
engine.set_optimization_level(OptimizationLevel::Simple);
|
||||||
|
|
||||||
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } }")?;
|
let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?;
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with("AST([], Module("));
|
assert!(format!("{:?}", ast).starts_with(r#"AST { statements: [Block([Const(IdentX { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#));
|
||||||
|
|
||||||
engine.set_optimization_level(OptimizationLevel::Full);
|
|
||||||
|
|
||||||
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
let ast = engine.compile("if 1 == 2 { 42 }")?;
|
||||||
|
|
||||||
assert!(format!("{:?}", ast).starts_with("AST([], Module("));
|
assert!(format!("{:?}", ast).starts_with("AST { statements: [], functions: Module("));
|
||||||
|
|
||||||
|
engine.set_optimization_level(OptimizationLevel::Full);
|
||||||
|
|
||||||
|
let ast = engine.compile("abs(-42)")?;
|
||||||
|
|
||||||
|
assert!(format!("{:?}", ast).starts_with(r"AST { statements: [Expr(IntegerConstant(42, 1:1))]"));
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,7 @@ use rhai::{Engine, EvalAltResult, RegisterFn, INT};
|
|||||||
use std::sync::{Arc, RwLock};
|
use std::sync::{Arc, RwLock};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_print() -> Result<(), Box<EvalAltResult>> {
|
fn test_print_debug() -> Result<(), Box<EvalAltResult>> {
|
||||||
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
let logbook = Arc::new(RwLock::new(Vec::<String>::new()));
|
||||||
|
|
||||||
// Redirect print/debug output to 'log'
|
// Redirect print/debug output to 'log'
|
||||||
@ -13,16 +13,20 @@ fn test_print() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
engine
|
engine
|
||||||
.on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s)))
|
.on_print(move |s| log1.write().unwrap().push(format!("entry: {}", s)))
|
||||||
.on_debug(move |s| log2.write().unwrap().push(format!("DEBUG: {}", s)));
|
.on_debug(move |s, pos| {
|
||||||
|
log2.write()
|
||||||
|
.unwrap()
|
||||||
|
.push(format!("DEBUG at {:?}: {}", pos, s))
|
||||||
|
});
|
||||||
|
|
||||||
// Evaluate script
|
// Evaluate script
|
||||||
engine.eval::<()>("print(40 + 2)")?;
|
engine.eval::<()>("print(40 + 2)")?;
|
||||||
engine.eval::<()>(r#"debug("hello!")"#)?;
|
engine.eval::<()>(r#"let x = "hello!"; debug(x)"#)?;
|
||||||
|
|
||||||
// 'logbook' captures all the 'print' and 'debug' output
|
// 'logbook' captures all the 'print' and 'debug' output
|
||||||
assert_eq!(logbook.read().unwrap().len(), 2);
|
assert_eq!(logbook.read().unwrap().len(), 2);
|
||||||
assert_eq!(logbook.read().unwrap()[0], "entry: 42");
|
assert_eq!(logbook.read().unwrap()[0], "entry: 42");
|
||||||
assert_eq!(logbook.read().unwrap()[1], r#"DEBUG: "hello!""#);
|
assert_eq!(logbook.read().unwrap()[1], r#"DEBUG at 1:19: "hello!""#);
|
||||||
|
|
||||||
for entry in logbook.read().unwrap().iter() {
|
for entry in logbook.read().unwrap().iter() {
|
||||||
println!("{}", entry);
|
println!("{}", entry);
|
||||||
|
Loading…
Reference in New Issue
Block a user