Move constancy into Dynamic.
This commit is contained in:
parent
8e8069f819
commit
f22a04fc74
@ -75,7 +75,7 @@ The Rhai Scripting Language
|
||||
5. [Variables](language/variables.md)
|
||||
6. [Constants](language/constants.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)
|
||||
10. [Switch Expression](language/switch.md)
|
||||
11. [While Loop](language/while.md)
|
||||
|
@ -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
|
||||
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
|
||||
-------------------------
|
||||
|
@ -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],
|
||||
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.
|
||||
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}}
|
||||
|
||||
|
||||
Compound Assignment Operators
|
||||
----------------------------
|
||||
|
||||
```rust
|
||||
let number = 9;
|
||||
|
||||
@ -64,3 +61,6 @@ my_obj += #{c:3, d:4, e: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 -
|
||||
being a constant only prevents it from being re-assigned or operated upon by Rhai;
|
||||
mutating it via a Rust function is still allowed.
|
||||
A custom type stored as a constant cannot be modified via script, but _can_ be modified via
|
||||
a registered Rust function that takes a first `&mut` parameter - because there is no way for
|
||||
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.
|
||||
|
@ -68,6 +68,8 @@
|
||||
|
||||
[variable]: {{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
|
||||
[strings]: {{rootUrl}}/language/strings-chars.md
|
||||
|
16
src/ast.rs
16
src/ast.rs
@ -1,6 +1,6 @@
|
||||
//! Module defining the AST (abstract syntax tree).
|
||||
|
||||
use crate::dynamic::Union;
|
||||
use crate::dynamic::{AccessType, Union};
|
||||
use crate::fn_native::shared_make_mut;
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::stdlib::{
|
||||
@ -940,10 +940,10 @@ impl Expr {
|
||||
Self::FloatConstant(x, _) => (*x).into(),
|
||||
Self::CharConstant(x, _) => (*x).into(),
|
||||
Self::StringConstant(x, _) => x.clone().into(),
|
||||
Self::FnPointer(x, _) => Dynamic(Union::FnPtr(Box::new(FnPtr::new_unchecked(
|
||||
x.clone(),
|
||||
Default::default(),
|
||||
)))),
|
||||
Self::FnPointer(x, _) => Dynamic(Union::FnPtr(
|
||||
Box::new(FnPtr::new_unchecked(x.clone(), Default::default())),
|
||||
AccessType::Constant,
|
||||
)),
|
||||
Self::BoolConstant(x, _) => (*x).into(),
|
||||
Self::Unit(_) => ().into(),
|
||||
|
||||
@ -954,7 +954,7 @@ impl Expr {
|
||||
x.len(),
|
||||
));
|
||||
arr.extend(x.iter().map(|v| v.get_constant_value().unwrap()));
|
||||
Dynamic(Union::Array(Box::new(arr)))
|
||||
Dynamic(Union::Array(Box::new(arr), AccessType::Constant))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -967,7 +967,7 @@ impl Expr {
|
||||
x.iter()
|
||||
.map(|(k, v)| (k.name.clone(), v.get_constant_value().unwrap())),
|
||||
);
|
||||
Dynamic(Union::Map(Box::new(map)))
|
||||
Dynamic(Union::Map(Box::new(map), AccessType::Constant))
|
||||
}
|
||||
|
||||
_ => return None,
|
||||
@ -1167,7 +1167,7 @@ mod tests {
|
||||
assert_eq!(size_of::<Option<crate::ast::Expr>>(), 16);
|
||||
assert_eq!(size_of::<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::ParseError>(), 16);
|
||||
assert_eq!(size_of::<crate::EvalAltResult>(), 72);
|
||||
|
429
src/dynamic.rs
429
src/dynamic.rs
@ -116,6 +116,25 @@ impl dyn Variant {
|
||||
}
|
||||
}
|
||||
|
||||
/// Type of an entry in the Scope.
|
||||
#[derive(Debug, Eq, PartialEq, Hash, Copy, Clone)]
|
||||
pub enum AccessType {
|
||||
/// Normal value.
|
||||
Normal,
|
||||
/// Immutable constant value.
|
||||
Constant,
|
||||
}
|
||||
|
||||
impl AccessType {
|
||||
/// Is the access type [`Constant`]?
|
||||
pub fn is_constant(self) -> bool {
|
||||
match self {
|
||||
Self::Normal => false,
|
||||
Self::Constant => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Dynamic type containing any value.
|
||||
pub struct Dynamic(pub(crate) Union);
|
||||
|
||||
@ -123,25 +142,25 @@ pub struct Dynamic(pub(crate) Union);
|
||||
///
|
||||
/// Most variants are boxed to reduce the size.
|
||||
pub enum Union {
|
||||
Unit(()),
|
||||
Bool(bool),
|
||||
Str(ImmutableString),
|
||||
Char(char),
|
||||
Int(INT),
|
||||
Unit((), AccessType),
|
||||
Bool(bool, AccessType),
|
||||
Str(ImmutableString, AccessType),
|
||||
Char(char, AccessType),
|
||||
Int(INT, AccessType),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Float(FLOAT),
|
||||
Float(FLOAT, AccessType),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Array(Box<Array>),
|
||||
Array(Box<Array>, AccessType),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Map(Box<Map>),
|
||||
FnPtr(Box<FnPtr>),
|
||||
Map(Box<Map>, AccessType),
|
||||
FnPtr(Box<FnPtr>, AccessType),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
TimeStamp(Box<Instant>),
|
||||
TimeStamp(Box<Instant>, AccessType),
|
||||
|
||||
Variant(Box<Box<dyn Variant>>),
|
||||
Variant(Box<Box<dyn Variant>>, AccessType),
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Shared(crate::Shared<crate::Locked<Dynamic>>),
|
||||
Shared(crate::Shared<crate::Locked<Dynamic>>, AccessType),
|
||||
}
|
||||
|
||||
/// Underlying [`Variant`] read guard for [`Dynamic`].
|
||||
@ -236,7 +255,7 @@ impl Dynamic {
|
||||
#[inline(always)]
|
||||
pub fn is_variant(&self) -> bool {
|
||||
match self.0 {
|
||||
Union::Variant(_) => true,
|
||||
Union::Variant(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -246,7 +265,7 @@ impl Dynamic {
|
||||
pub fn is_shared(&self) -> bool {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(_) => true,
|
||||
Union::Shared(_, _) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -272,29 +291,29 @@ impl Dynamic {
|
||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||
pub fn type_id(&self) -> TypeId {
|
||||
match &self.0 {
|
||||
Union::Unit(_) => TypeId::of::<()>(),
|
||||
Union::Bool(_) => TypeId::of::<bool>(),
|
||||
Union::Str(_) => TypeId::of::<ImmutableString>(),
|
||||
Union::Char(_) => TypeId::of::<char>(),
|
||||
Union::Int(_) => TypeId::of::<INT>(),
|
||||
Union::Unit(_, _) => TypeId::of::<()>(),
|
||||
Union::Bool(_, _) => TypeId::of::<bool>(),
|
||||
Union::Str(_, _) => TypeId::of::<ImmutableString>(),
|
||||
Union::Char(_, _) => TypeId::of::<char>(),
|
||||
Union::Int(_, _) => TypeId::of::<INT>(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_) => TypeId::of::<FLOAT>(),
|
||||
Union::Float(_, _) => TypeId::of::<FLOAT>(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_) => TypeId::of::<Array>(),
|
||||
Union::Array(_, _) => TypeId::of::<Array>(),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_) => TypeId::of::<Map>(),
|
||||
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
||||
Union::Map(_, _) => TypeId::of::<Map>(),
|
||||
Union::FnPtr(_, _) => TypeId::of::<FnPtr>(),
|
||||
#[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 = "sync"))]
|
||||
Union::Shared(cell) => (*cell.borrow()).type_id(),
|
||||
Union::Shared(cell, _) => (*cell.borrow()).type_id(),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[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`].
|
||||
@ -305,32 +324,32 @@ impl Dynamic {
|
||||
/// Otherwise, this call panics if the data is currently borrowed for write.
|
||||
pub fn type_name(&self) -> &'static str {
|
||||
match &self.0 {
|
||||
Union::Unit(_) => "()",
|
||||
Union::Bool(_) => "bool",
|
||||
Union::Str(_) => "string",
|
||||
Union::Char(_) => "char",
|
||||
Union::Int(_) => type_name::<INT>(),
|
||||
Union::Unit(_, _) => "()",
|
||||
Union::Bool(_, _) => "bool",
|
||||
Union::Str(_, _) => "string",
|
||||
Union::Char(_, _) => "char",
|
||||
Union::Int(_, _) => type_name::<INT>(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(_) => type_name::<FLOAT>(),
|
||||
Union::Float(_, _) => type_name::<FLOAT>(),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(_) => "array",
|
||||
Union::Array(_, _) => "array",
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(_) => "map",
|
||||
Union::FnPtr(_) => "Fn",
|
||||
Union::Map(_, _) => "map",
|
||||
Union::FnPtr(_, _) => "Fn",
|
||||
#[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 = "sync"))]
|
||||
Union::Shared(cell) => cell
|
||||
Union::Shared(cell, _) => cell
|
||||
.try_borrow()
|
||||
.map(|v| (*v).type_name())
|
||||
.unwrap_or("<shared>"),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[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);
|
||||
|
||||
match &self.0 {
|
||||
Union::Unit(_) => ().hash(state),
|
||||
Union::Bool(value) => value.hash(state),
|
||||
Union::Str(s) => s.hash(state),
|
||||
Union::Char(ch) => ch.hash(state),
|
||||
Union::Int(i) => i.hash(state),
|
||||
Union::Unit(_, _) => ().hash(state),
|
||||
Union::Bool(value, _) => value.hash(state),
|
||||
Union::Str(s, _) => s.hash(state),
|
||||
Union::Char(ch, _) => ch.hash(state),
|
||||
Union::Int(i, _) => i.hash(state),
|
||||
#[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"))]
|
||||
Union::Array(a) => (**a).hash(state),
|
||||
Union::Array(a, _) => (**a).hash(state),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(m) => {
|
||||
Union::Map(m, _) => {
|
||||
let mut buf: crate::StaticVec<_> = m.iter().collect();
|
||||
buf.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||
|
||||
@ -362,10 +381,10 @@ impl Hash for Dynamic {
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(not(feature = "sync"))]
|
||||
Union::Shared(cell) => (*cell.borrow()).hash(state),
|
||||
Union::Shared(cell, _) => (*cell.borrow()).hash(state),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[cfg(feature = "sync")]
|
||||
Union::Shared(cell) => (*cell.read().unwrap()).hash(state),
|
||||
Union::Shared(cell, _) => (*cell.read().unwrap()).hash(state),
|
||||
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
@ -404,29 +423,29 @@ pub(crate) fn map_std_type_name(name: &str) -> &str {
|
||||
impl fmt::Display for Dynamic {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 {
|
||||
Union::Unit(_) => write!(f, ""),
|
||||
Union::Bool(value) => fmt::Display::fmt(value, f),
|
||||
Union::Str(value) => fmt::Display::fmt(value, f),
|
||||
Union::Char(value) => fmt::Display::fmt(value, f),
|
||||
Union::Int(value) => fmt::Display::fmt(value, f),
|
||||
Union::Unit(_, _) => write!(f, ""),
|
||||
Union::Bool(value, _) => fmt::Display::fmt(value, f),
|
||||
Union::Str(value, _) => fmt::Display::fmt(value, f),
|
||||
Union::Char(value, _) => fmt::Display::fmt(value, f),
|
||||
Union::Int(value, _) => fmt::Display::fmt(value, f),
|
||||
#[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"))]
|
||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Array(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(value) => {
|
||||
Union::Map(value, _) => {
|
||||
f.write_str("#")?;
|
||||
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"))]
|
||||
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 = "sync"))]
|
||||
Union::Shared(cell) => {
|
||||
Union::Shared(cell, _) => {
|
||||
if let Ok(v) = cell.try_borrow() {
|
||||
fmt::Display::fmt(&*v, f)
|
||||
} else {
|
||||
@ -435,7 +454,7 @@ impl fmt::Display for Dynamic {
|
||||
}
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[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 {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match &self.0 {
|
||||
Union::Unit(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Bool(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Str(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Char(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Int(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Unit(value, _) => fmt::Debug::fmt(value, f),
|
||||
Union::Bool(value, _) => fmt::Debug::fmt(value, f),
|
||||
Union::Str(value, _) => fmt::Debug::fmt(value, f),
|
||||
Union::Char(value, _) => fmt::Debug::fmt(value, f),
|
||||
Union::Int(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[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"))]
|
||||
Union::Array(value) => fmt::Debug::fmt(value, f),
|
||||
Union::Array(value, _) => fmt::Debug::fmt(value, f),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(value) => {
|
||||
Union::Map(value, _) => {
|
||||
f.write_str("#")?;
|
||||
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"))]
|
||||
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 = "sync"))]
|
||||
Union::Shared(cell) => {
|
||||
Union::Shared(cell, _) => {
|
||||
if let Ok(v) = cell.try_borrow() {
|
||||
write!(f, "{:?} (shared)", *v)
|
||||
} else {
|
||||
@ -474,33 +493,40 @@ impl fmt::Debug for Dynamic {
|
||||
}
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[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 {
|
||||
/// Clone the [`Dynamic`] value.
|
||||
///
|
||||
/// ## WARNING
|
||||
///
|
||||
/// The cloned copy is marked [`AccessType::Normal`] even if the original is constant.
|
||||
fn clone(&self) -> Self {
|
||||
match self.0 {
|
||||
Union::Unit(value) => Self(Union::Unit(value)),
|
||||
Union::Bool(value) => Self(Union::Bool(value)),
|
||||
Union::Str(ref value) => Self(Union::Str(value.clone())),
|
||||
Union::Char(value) => Self(Union::Char(value)),
|
||||
Union::Int(value) => Self(Union::Int(value)),
|
||||
Union::Unit(value, _) => Self(Union::Unit(value, AccessType::Normal)),
|
||||
Union::Bool(value, _) => Self(Union::Bool(value, AccessType::Normal)),
|
||||
Union::Str(ref value, _) => Self(Union::Str(value.clone(), AccessType::Normal)),
|
||||
Union::Char(value, _) => Self(Union::Char(value, AccessType::Normal)),
|
||||
Union::Int(value, _) => Self(Union::Int(value, AccessType::Normal)),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
Union::Float(value) => Self(Union::Float(value)),
|
||||
Union::Float(value, _) => Self(Union::Float(value, AccessType::Normal)),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(ref value) => Self(Union::Array(value.clone())),
|
||||
Union::Array(ref value, _) => Self(Union::Array(value.clone(), AccessType::Normal)),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
||||
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
||||
Union::Map(ref value, _) => Self(Union::Map(value.clone(), AccessType::Normal)),
|
||||
Union::FnPtr(ref value, _) => Self(Union::FnPtr(value.clone(), AccessType::Normal)),
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
Union::TimeStamp(ref value) => Self(Union::TimeStamp(value.clone())),
|
||||
Union::TimeStamp(ref value, _) => {
|
||||
Self(Union::TimeStamp(value.clone(), AccessType::Normal))
|
||||
}
|
||||
|
||||
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
||||
Union::Variant(ref value, _) => (***value).clone_into_dynamic(),
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell) => Self(Union::Shared(cell.clone())),
|
||||
Union::Shared(ref cell, _) => Self(Union::Shared(cell.clone(), AccessType::Normal)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -514,27 +540,65 @@ impl Default for Dynamic {
|
||||
|
||||
impl Dynamic {
|
||||
/// A [`Dynamic`] containing a `()`.
|
||||
pub const UNIT: Dynamic = Self(Union::Unit(()));
|
||||
pub const UNIT: Dynamic = Self(Union::Unit((), AccessType::Normal));
|
||||
/// A [`Dynamic`] containing a `true`.
|
||||
pub const TRUE: Dynamic = Self(Union::Bool(true));
|
||||
pub const TRUE: Dynamic = Self(Union::Bool(true, AccessType::Normal));
|
||||
/// A [`Dynamic`] containing a [`false`].
|
||||
pub const FALSE: Dynamic = Self(Union::Bool(false));
|
||||
pub const FALSE: Dynamic = Self(Union::Bool(false, AccessType::Normal));
|
||||
/// A [`Dynamic`] containing the integer zero.
|
||||
pub const ZERO: Dynamic = Self(Union::Int(0));
|
||||
pub const ZERO: Dynamic = Self(Union::Int(0, AccessType::Normal));
|
||||
/// A [`Dynamic`] containing the integer one.
|
||||
pub const ONE: Dynamic = Self(Union::Int(1));
|
||||
pub const ONE: Dynamic = Self(Union::Int(1, AccessType::Normal));
|
||||
/// 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, AccessType::Normal));
|
||||
/// A [`Dynamic`] containing the floating-point zero.
|
||||
#[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, AccessType::Normal));
|
||||
/// A [`Dynamic`] containing the floating-point one.
|
||||
#[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, AccessType::Normal));
|
||||
/// A [`Dynamic`] containing the floating-point negative one.
|
||||
#[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, AccessType::Normal));
|
||||
|
||||
/// Get the [`AccessType`] for this [`Dynamic`].
|
||||
pub(crate) fn access_type(&self) -> AccessType {
|
||||
match self.0 {
|
||||
Union::Unit(_, access)
|
||||
| Union::Bool(_, access)
|
||||
| Union::Str(_, access)
|
||||
| Union::Char(_, access)
|
||||
| Union::Int(_, access)
|
||||
| Union::Float(_, access)
|
||||
| Union::Array(_, access)
|
||||
| Union::Map(_, access)
|
||||
| Union::FnPtr(_, access)
|
||||
| Union::TimeStamp(_, access)
|
||||
| Union::Variant(_, access)
|
||||
| Union::Shared(_, access) => access,
|
||||
}
|
||||
}
|
||||
/// Set the [`AccessType`] for this [`Dynamic`].
|
||||
pub(crate) fn set_access_type(&mut self, typ: AccessType) {
|
||||
match &mut self.0 {
|
||||
Union::Unit(_, access)
|
||||
| Union::Bool(_, access)
|
||||
| Union::Str(_, access)
|
||||
| Union::Char(_, access)
|
||||
| Union::Int(_, access)
|
||||
| Union::Float(_, access)
|
||||
| Union::Array(_, access)
|
||||
| Union::Map(_, access)
|
||||
| Union::FnPtr(_, access)
|
||||
| Union::TimeStamp(_, access)
|
||||
| Union::Variant(_, access)
|
||||
| Union::Shared(_, access) => *access = typ,
|
||||
}
|
||||
}
|
||||
/// Is this [`Dynamic`] constant?
|
||||
pub(crate) fn is_constant(&self) -> bool {
|
||||
self.access_type().is_constant()
|
||||
}
|
||||
/// Create a [`Dynamic`] from any type. A [`Dynamic`] value is simply returned as is.
|
||||
///
|
||||
/// # Safety
|
||||
@ -651,7 +715,7 @@ impl Dynamic {
|
||||
}
|
||||
}
|
||||
|
||||
Self(Union::Variant(Box::new(boxed)))
|
||||
Self(Union::Variant(Box::new(boxed), AccessType::Normal))
|
||||
}
|
||||
/// 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.
|
||||
@ -668,10 +732,12 @@ impl Dynamic {
|
||||
/// Panics under the `no_closure` feature.
|
||||
#[inline(always)]
|
||||
pub fn into_shared(self) -> Self {
|
||||
let _access = self.access_type();
|
||||
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
return match self.0 {
|
||||
Union::Shared(..) => self,
|
||||
_ => Self(Union::Shared(crate::Locked::new(self).into())),
|
||||
Union::Shared(_, _) => self,
|
||||
_ => Self(Union::Shared(crate::Locked::new(self).into(), _access)),
|
||||
};
|
||||
|
||||
#[cfg(feature = "no_closure")]
|
||||
@ -707,11 +773,11 @@ impl Dynamic {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
#[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(feature = "sync")]
|
||||
Union::Shared(cell) => return cell.read().unwrap().clone().try_cast(),
|
||||
Union::Shared(cell, _) => return cell.read().unwrap().clone().try_cast(),
|
||||
_ => (),
|
||||
}
|
||||
|
||||
@ -721,7 +787,7 @@ impl Dynamic {
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||
return match self.0 {
|
||||
Union::Int(value) => unsafe_try_cast(value),
|
||||
Union::Int(value, _) => unsafe_try_cast(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -729,35 +795,35 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||
return match self.0 {
|
||||
Union::Float(value) => unsafe_try_cast(value),
|
||||
Union::Float(value, _) => unsafe_try_cast(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match self.0 {
|
||||
Union::Bool(value) => unsafe_try_cast(value),
|
||||
Union::Bool(value, _) => unsafe_try_cast(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||
return match self.0 {
|
||||
Union::Str(value) => unsafe_try_cast(value),
|
||||
Union::Str(value, _) => unsafe_try_cast(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
return match self.0 {
|
||||
Union::Str(value) => unsafe_try_cast(value.into_owned()),
|
||||
Union::Str(value, _) => unsafe_try_cast(value.into_owned()),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||
return match self.0 {
|
||||
Union::Char(value) => unsafe_try_cast(value),
|
||||
Union::Char(value, _) => unsafe_try_cast(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -765,7 +831,7 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
@ -773,14 +839,14 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
@ -788,22 +854,22 @@ impl Dynamic {
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
return match self.0 {
|
||||
Union::Unit(value) => unsafe_try_cast(value),
|
||||
Union::Unit(value, _) => unsafe_try_cast(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
|
||||
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"))]
|
||||
Union::Shared(_) => unreachable!(),
|
||||
Union::Shared(_, _) => unreachable!(),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -859,7 +925,7 @@ impl Dynamic {
|
||||
pub fn flatten_clone(&self) -> Self {
|
||||
match &self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(cell) => {
|
||||
Union::Shared(cell, _) => {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return cell.borrow().clone();
|
||||
|
||||
@ -879,7 +945,7 @@ impl Dynamic {
|
||||
pub fn flatten(self) -> Self {
|
||||
match self.0 {
|
||||
#[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| {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return cell.borrow().clone();
|
||||
@ -907,7 +973,7 @@ impl Dynamic {
|
||||
pub fn is_locked(&self) -> bool {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref _cell) => {
|
||||
Union::Shared(ref _cell, _) => {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
return _cell.try_borrow().is_err();
|
||||
|
||||
@ -930,7 +996,7 @@ impl Dynamic {
|
||||
pub fn read_lock<T: Variant + Clone>(&self) -> Option<DynamicReadLock<T>> {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell) => {
|
||||
Union::Shared(ref cell, _) => {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
let data = cell.borrow();
|
||||
#[cfg(feature = "sync")]
|
||||
@ -962,7 +1028,7 @@ impl Dynamic {
|
||||
pub fn write_lock<T: Variant + Clone>(&mut self) -> Option<DynamicWriteLock<T>> {
|
||||
match self.0 {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(ref cell) => {
|
||||
Union::Shared(ref cell, _) => {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
let data = cell.borrow_mut();
|
||||
#[cfg(feature = "sync")]
|
||||
@ -991,71 +1057,71 @@ impl Dynamic {
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||
return match &self.0 {
|
||||
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::Int(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||
return match &self.0 {
|
||||
Union::Float(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::Float(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match &self.0 {
|
||||
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::Bool(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||
return match &self.0 {
|
||||
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<String>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||
return match &self.0 {
|
||||
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::Char(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
return match &self.0 {
|
||||
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||
Union::Unit(value, _) => <dyn Any>::downcast_ref::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -1064,9 +1130,9 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
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"))]
|
||||
Union::Shared(_) => None,
|
||||
Union::Shared(_, _) => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -1080,65 +1146,65 @@ impl Dynamic {
|
||||
|
||||
if TypeId::of::<T>() == TypeId::of::<INT>() {
|
||||
return match &mut self.0 {
|
||||
Union::Int(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::Int(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<FLOAT>() {
|
||||
return match &mut self.0 {
|
||||
Union::Float(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::Float(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<bool>() {
|
||||
return match &mut self.0 {
|
||||
Union::Bool(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::Bool(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<ImmutableString>() {
|
||||
return match &mut self.0 {
|
||||
Union::Str(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::Str(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<char>() {
|
||||
return match &mut self.0 {
|
||||
Union::Char(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::Char(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Map>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<FnPtr>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Instant>() {
|
||||
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,
|
||||
};
|
||||
}
|
||||
if TypeId::of::<T>() == TypeId::of::<()>() {
|
||||
return match &mut self.0 {
|
||||
Union::Unit(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||
Union::Unit(value, _) => <dyn Any>::downcast_mut::<T>(value),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
@ -1147,9 +1213,9 @@ impl Dynamic {
|
||||
}
|
||||
|
||||
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"))]
|
||||
Union::Shared(_) => None,
|
||||
Union::Shared(_, _) => None,
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -1158,9 +1224,9 @@ impl Dynamic {
|
||||
#[inline(always)]
|
||||
pub fn as_int(&self) -> Result<INT, &'static str> {
|
||||
match self.0 {
|
||||
Union::Int(n) => Ok(n),
|
||||
Union::Int(n, _) => Ok(n),
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
@ -1170,9 +1236,9 @@ impl Dynamic {
|
||||
#[inline(always)]
|
||||
pub fn as_float(&self) -> Result<FLOAT, &'static str> {
|
||||
match self.0 {
|
||||
Union::Float(n) => Ok(n),
|
||||
Union::Float(n, _) => Ok(n),
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
@ -1181,9 +1247,9 @@ impl Dynamic {
|
||||
#[inline(always)]
|
||||
pub fn as_bool(&self) -> Result<bool, &'static str> {
|
||||
match self.0 {
|
||||
Union::Bool(b) => Ok(b),
|
||||
Union::Bool(b, _) => Ok(b),
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
@ -1192,9 +1258,9 @@ impl Dynamic {
|
||||
#[inline(always)]
|
||||
pub fn as_char(&self) -> Result<char, &'static str> {
|
||||
match self.0 {
|
||||
Union::Char(n) => Ok(n),
|
||||
Union::Char(n, _) => Ok(n),
|
||||
#[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()),
|
||||
}
|
||||
}
|
||||
@ -1205,8 +1271,8 @@ impl Dynamic {
|
||||
#[inline(always)]
|
||||
pub fn as_str(&self) -> Result<&str, &'static str> {
|
||||
match &self.0 {
|
||||
Union::Str(s) => Ok(s),
|
||||
Union::FnPtr(f) => Ok(f.fn_name()),
|
||||
Union::Str(s, _) => Ok(s),
|
||||
Union::FnPtr(f, _) => Ok(f.fn_name()),
|
||||
_ => Err(self.type_name()),
|
||||
}
|
||||
}
|
||||
@ -1223,16 +1289,16 @@ impl Dynamic {
|
||||
#[inline]
|
||||
pub fn take_immutable_string(self) -> Result<ImmutableString, &'static str> {
|
||||
match self.0 {
|
||||
Union::Str(s) => Ok(s),
|
||||
Union::FnPtr(f) => Ok(f.take_data().0),
|
||||
Union::Str(s, _) => Ok(s),
|
||||
Union::FnPtr(f, _) => Ok(f.take_data().0),
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Union::Shared(cell) => {
|
||||
Union::Shared(cell, _) => {
|
||||
#[cfg(not(feature = "sync"))]
|
||||
{
|
||||
let inner = cell.borrow();
|
||||
match &inner.0 {
|
||||
Union::Str(s) => Ok(s.clone()),
|
||||
Union::FnPtr(f) => Ok(f.clone().take_data().0),
|
||||
Union::Str(s, _) => Ok(s.clone()),
|
||||
Union::FnPtr(f, _) => Ok(f.clone().take_data().0),
|
||||
_ => Err((*inner).type_name()),
|
||||
}
|
||||
}
|
||||
@ -1240,8 +1306,8 @@ impl Dynamic {
|
||||
{
|
||||
let inner = cell.read().unwrap();
|
||||
match &inner.0 {
|
||||
Union::Str(s) => Ok(s.clone()),
|
||||
Union::FnPtr(f) => Ok(f.clone().take_data().0),
|
||||
Union::Str(s, _) => Ok(s.clone()),
|
||||
Union::FnPtr(f, _) => Ok(f.clone().take_data().0),
|
||||
_ => Err((*inner).type_name()),
|
||||
}
|
||||
}
|
||||
@ -1254,56 +1320,58 @@ impl Dynamic {
|
||||
impl From<()> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: ()) -> Self {
|
||||
Self(Union::Unit(value))
|
||||
Self(Union::Unit(value, AccessType::Normal))
|
||||
}
|
||||
}
|
||||
impl From<bool> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: bool) -> Self {
|
||||
Self(Union::Bool(value))
|
||||
Self(Union::Bool(value, AccessType::Normal))
|
||||
}
|
||||
}
|
||||
impl From<INT> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: INT) -> Self {
|
||||
Self(Union::Int(value))
|
||||
Self(Union::Int(value, AccessType::Normal))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
impl From<FLOAT> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: FLOAT) -> Self {
|
||||
Self(Union::Float(value))
|
||||
Self(Union::Float(value, AccessType::Normal))
|
||||
}
|
||||
}
|
||||
impl From<char> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: char) -> Self {
|
||||
Self(Union::Char(value))
|
||||
Self(Union::Char(value, AccessType::Normal))
|
||||
}
|
||||
}
|
||||
impl<S: Into<ImmutableString>> From<S> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: S) -> Self {
|
||||
Self(Union::Str(value.into()))
|
||||
Self(Union::Str(value.into(), AccessType::Normal))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl<T: Variant + Clone> From<crate::stdlib::vec::Vec<T>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: crate::stdlib::vec::Vec<T>) -> Self {
|
||||
Self(Union::Array(Box::new(
|
||||
value.into_iter().map(Dynamic::from).collect(),
|
||||
)))
|
||||
Self(Union::Array(
|
||||
Box::new(value.into_iter().map(Dynamic::from).collect()),
|
||||
AccessType::Normal,
|
||||
))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
impl<T: Variant + Clone> From<&[T]> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: &[T]) -> Self {
|
||||
Self(Union::Array(Box::new(
|
||||
value.iter().cloned().map(Dynamic::from).collect(),
|
||||
)))
|
||||
Self(Union::Array(
|
||||
Box::new(value.iter().cloned().map(Dynamic::from).collect()),
|
||||
AccessType::Normal,
|
||||
))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -1312,30 +1380,33 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<crate::stdlib::collectio
|
||||
{
|
||||
#[inline(always)]
|
||||
fn from(value: crate::stdlib::collections::HashMap<K, T>) -> Self {
|
||||
Self(Union::Map(Box::new(
|
||||
Self(Union::Map(
|
||||
Box::new(
|
||||
value
|
||||
.into_iter()
|
||||
.map(|(k, v)| (k.into(), Dynamic::from(v)))
|
||||
.collect(),
|
||||
)))
|
||||
),
|
||||
AccessType::Normal,
|
||||
))
|
||||
}
|
||||
}
|
||||
impl From<FnPtr> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: FnPtr) -> Self {
|
||||
Self(Union::FnPtr(Box::new(value)))
|
||||
Self(Union::FnPtr(Box::new(value), AccessType::Normal))
|
||||
}
|
||||
}
|
||||
impl From<Box<FnPtr>> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Box<FnPtr>) -> Self {
|
||||
Self(Union::FnPtr(value))
|
||||
Self(Union::FnPtr(value, AccessType::Normal))
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
impl From<Instant> for Dynamic {
|
||||
#[inline(always)]
|
||||
fn from(value: Instant) -> Self {
|
||||
Self(Union::TimeStamp(Box::new(value)))
|
||||
Self(Union::TimeStamp(Box::new(value), AccessType::Normal))
|
||||
}
|
||||
}
|
||||
|
130
src/engine.rs
130
src/engine.rs
@ -1,14 +1,13 @@
|
||||
//! Main module defining the script evaluation [`Engine`].
|
||||
|
||||
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, AccessType, Union, Variant};
|
||||
use crate::fn_call::run_builtin_op_assignment;
|
||||
use crate::fn_native::{CallableFunction, Callback, IteratorFn, OnVarCallback};
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::packages::{Package, PackagesCollection, StandardPackage};
|
||||
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
|
||||
use crate::scope::EntryType as ScopeEntryType;
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
borrow::Cow,
|
||||
@ -833,7 +832,7 @@ impl Engine {
|
||||
lib: &[&Module],
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
expr: &'a Expr,
|
||||
) -> Result<(Target<'s>, &'a str, ScopeEntryType, Position), Box<EvalAltResult>> {
|
||||
) -> Result<(Target<'s>, &'a str, Position), Box<EvalAltResult>> {
|
||||
match expr {
|
||||
Expr::Variable(v) => match v.as_ref() {
|
||||
// Qualified variable
|
||||
@ -850,7 +849,9 @@ impl Engine {
|
||||
})?;
|
||||
|
||||
// Module variables are constant
|
||||
Ok((target.clone().into(), name, ScopeEntryType::Constant, *pos))
|
||||
let mut target = target.clone();
|
||||
target.set_access_type(AccessType::Constant);
|
||||
Ok((target.into(), name, *pos))
|
||||
}
|
||||
// Normal variable access
|
||||
_ => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
|
||||
@ -868,7 +869,7 @@ impl Engine {
|
||||
lib: &[&Module],
|
||||
this_ptr: &'s mut Option<&mut Dynamic>,
|
||||
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 {
|
||||
Expr::Variable(v) => v.as_ref(),
|
||||
_ => unreachable!(),
|
||||
@ -877,7 +878,7 @@ impl Engine {
|
||||
// Check if the variable is `this`
|
||||
if name.as_str() == KEYWORD_THIS {
|
||||
if let Some(val) = this_ptr {
|
||||
return Ok(((*val).into(), KEYWORD_THIS, ScopeEntryType::Normal, *pos));
|
||||
return Ok(((*val).into(), KEYWORD_THIS, *pos));
|
||||
} else {
|
||||
return EvalAltResult::ErrorUnboundThis(*pos).into();
|
||||
}
|
||||
@ -901,10 +902,11 @@ impl Engine {
|
||||
this_ptr,
|
||||
level: 0,
|
||||
};
|
||||
if let Some(result) =
|
||||
if let Some(mut result) =
|
||||
resolve_var(name, index, &context).map_err(|err| err.fill_position(*pos))?
|
||||
{
|
||||
return Ok((result.into(), name, ScopeEntryType::Constant, *pos));
|
||||
result.set_access_type(AccessType::Constant);
|
||||
return Ok((result.into(), name, *pos));
|
||||
}
|
||||
}
|
||||
|
||||
@ -918,7 +920,7 @@ impl Engine {
|
||||
.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
|
||||
// when the object variable is also used as a parameter.
|
||||
@ -926,7 +928,7 @@ impl Engine {
|
||||
// return EvalAltResult::ErrorDataRace(name.into(), *pos).into();
|
||||
// }
|
||||
|
||||
Ok((val.into(), name, typ, *pos))
|
||||
Ok((val.into(), name, *pos))
|
||||
}
|
||||
|
||||
/// Chain-evaluate a dot/index chain.
|
||||
@ -1279,17 +1281,14 @@ impl Engine {
|
||||
self.inc_operations(state)
|
||||
.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)?;
|
||||
|
||||
// Constants cannot be modified
|
||||
match typ {
|
||||
ScopeEntryType::Constant if new_val.is_some() => {
|
||||
if target.as_ref().is_constant() && new_val.is_some() {
|
||||
return EvalAltResult::ErrorAssignmentToConstant(var_name.to_string(), pos)
|
||||
.into();
|
||||
}
|
||||
ScopeEntryType::Constant | ScopeEntryType::Normal => (),
|
||||
}
|
||||
|
||||
let obj_ptr = &mut target.into();
|
||||
self.eval_dot_index_chain_helper(
|
||||
@ -1410,7 +1409,7 @@ impl Engine {
|
||||
|
||||
match target {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Dynamic(Union::Array(arr)) => {
|
||||
Dynamic(Union::Array(arr, _)) => {
|
||||
// val_array[idx]
|
||||
let index = idx
|
||||
.as_int()
|
||||
@ -1430,7 +1429,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Dynamic(Union::Map(map)) => {
|
||||
Dynamic(Union::Map(map, _)) => {
|
||||
// val_map[idx]
|
||||
Ok(if _create {
|
||||
let index = idx.take_immutable_string().map_err(|err| {
|
||||
@ -1450,7 +1449,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Dynamic(Union::Str(s)) => {
|
||||
Dynamic(Union::Str(s, _)) => {
|
||||
// val_string[idx]
|
||||
let chars_len = s.chars().count();
|
||||
let index = idx
|
||||
@ -1517,7 +1516,7 @@ impl Engine {
|
||||
|
||||
match rhs_value {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Dynamic(Union::Array(mut rhs_value)) => {
|
||||
Dynamic(Union::Array(mut rhs_value, _)) => {
|
||||
// Call the `==` operator to compare each value
|
||||
let def_value = Some(false.into());
|
||||
let def_value = def_value.as_ref();
|
||||
@ -1545,16 +1544,16 @@ impl Engine {
|
||||
Ok(false.into())
|
||||
}
|
||||
#[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
|
||||
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::Str(s, _)) => Ok(rhs_value.contains_key(&s).into()),
|
||||
Dynamic(Union::Char(c, _)) => Ok(rhs_value.contains_key(&c.to_string()).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
|
||||
Dynamic(Union::Str(s)) => Ok(rhs_value.contains(s.as_str()).into()),
|
||||
Dynamic(Union::Char(c)) => Ok(rhs_value.contains(c).into()),
|
||||
Dynamic(Union::Str(s, _)) => Ok(rhs_value.contains(s.as_str()).into()),
|
||||
Dynamic(Union::Char(c, _)) => Ok(rhs_value.contains(c).into()),
|
||||
_ => EvalAltResult::ErrorInExpr(lhs.position()).into(),
|
||||
},
|
||||
_ => EvalAltResult::ErrorInExpr(rhs.position()).into(),
|
||||
@ -1576,17 +1575,15 @@ impl Engine {
|
||||
match expr {
|
||||
// var - point directly to the value
|
||||
Expr::Variable(_) => {
|
||||
let (target, _, typ, pos) =
|
||||
let (mut target, _, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||
|
||||
Ok((
|
||||
match typ {
|
||||
// If necessary, constants are cloned
|
||||
ScopeEntryType::Constant if no_const => target.into_owned(),
|
||||
_ => target,
|
||||
},
|
||||
pos,
|
||||
))
|
||||
if target.as_ref().is_constant() {
|
||||
target = target.into_owned();
|
||||
}
|
||||
|
||||
Ok((target, pos))
|
||||
}
|
||||
// var[...]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -1706,8 +1703,7 @@ impl Engine {
|
||||
}
|
||||
}
|
||||
Expr::Variable(_) => {
|
||||
let (val, _, _, _) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||
let (val, _, _) = self.search_namespace(scope, mods, state, lib, this_ptr, expr)?;
|
||||
Ok(val.take_or_clone())
|
||||
}
|
||||
Expr::Property(_) => unreachable!(),
|
||||
@ -1736,7 +1732,7 @@ impl Engine {
|
||||
for item in x.as_ref() {
|
||||
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), AccessType::Normal)))
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
@ -1749,7 +1745,7 @@ impl Engine {
|
||||
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), AccessType::Normal)))
|
||||
}
|
||||
|
||||
// Normal function call
|
||||
@ -1913,7 +1909,7 @@ impl Engine {
|
||||
let mut rhs_val = self
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?
|
||||
.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)?;
|
||||
|
||||
if !lhs_ptr.is_ref() {
|
||||
@ -1923,29 +1919,28 @@ impl Engine {
|
||||
self.inc_operations(state)
|
||||
.map_err(|err| err.fill_position(pos))?;
|
||||
|
||||
match typ {
|
||||
if lhs_ptr.as_ref().is_constant() {
|
||||
// Assignment to constant variable
|
||||
ScopeEntryType::Constant => Err(Box::new(
|
||||
EvalAltResult::ErrorAssignmentToConstant(name.to_string(), pos),
|
||||
)),
|
||||
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
|
||||
name.to_string(),
|
||||
pos,
|
||||
)))
|
||||
} else if op.is_empty() {
|
||||
// Normal assignment
|
||||
ScopeEntryType::Normal if op.is_empty() => {
|
||||
if cfg!(not(feature = "no_closure")) && lhs_ptr.is_shared() {
|
||||
*lhs_ptr.as_mut().write_lock::<Dynamic>().unwrap() = rhs_val;
|
||||
} else {
|
||||
*lhs_ptr.as_mut() = rhs_val;
|
||||
}
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
} else {
|
||||
// Op-assignment - in order of precedence:
|
||||
ScopeEntryType::Normal => {
|
||||
// 1) Native registered overriding function
|
||||
// 2) Built-in implementation
|
||||
// 3) Map to `var = var op rhs`
|
||||
|
||||
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
|
||||
let arg_types =
|
||||
once(lhs_ptr.as_mut().type_id()).chain(once(rhs_val.type_id()));
|
||||
let arg_types = once(lhs_ptr.as_mut().type_id()).chain(once(rhs_val.type_id()));
|
||||
let hash_fn = calc_native_fn_hash(empty(), op, arg_types);
|
||||
|
||||
match self
|
||||
@ -1989,8 +1984,8 @@ impl Engine {
|
||||
// Run function
|
||||
let (value, _) = self
|
||||
.exec_fn_call(
|
||||
mods, state, lib, op, 0, args, false, false, false, None,
|
||||
None, level,
|
||||
mods, state, lib, op, 0, args, false, false, false, None, None,
|
||||
level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*op_pos))?;
|
||||
|
||||
@ -2006,7 +2001,6 @@ impl Engine {
|
||||
Ok(Dynamic::UNIT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// lhs op= rhs
|
||||
Stmt::Assignment(x, op_pos) => {
|
||||
@ -2175,7 +2169,7 @@ impl Engine {
|
||||
state.scope_level += 1;
|
||||
|
||||
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();
|
||||
if cfg!(not(feature = "no_closure")) && loop_var.is_shared() {
|
||||
@ -2249,8 +2243,10 @@ impl Engine {
|
||||
.map(|_| ().into());
|
||||
|
||||
if let Some(result_err) = result.as_ref().err() {
|
||||
if let EvalAltResult::ErrorRuntime(Dynamic(Union::Unit(_)), pos) =
|
||||
result_err.as_ref()
|
||||
if let EvalAltResult::ErrorRuntime(
|
||||
Dynamic(Union::Unit(_, _)),
|
||||
pos,
|
||||
) = result_err.as_ref()
|
||||
{
|
||||
err.set_position(*pos);
|
||||
result = Err(Box::new(err));
|
||||
@ -2293,8 +2289,8 @@ impl Engine {
|
||||
// Let/const statement
|
||||
Stmt::Let(var_def, expr, export, _) | Stmt::Const(var_def, expr, export, _) => {
|
||||
let entry_type = match stmt {
|
||||
Stmt::Let(_, _, _, _) => ScopeEntryType::Normal,
|
||||
Stmt::Const(_, _, _, _) => ScopeEntryType::Constant,
|
||||
Stmt::Let(_, _, _, _) => AccessType::Normal,
|
||||
Stmt::Const(_, _, _, _) => AccessType::Constant,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
@ -2388,8 +2384,8 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_closure"))]
|
||||
Stmt::Share(x) => {
|
||||
match scope.get_index(&x.name) {
|
||||
Some((index, ScopeEntryType::Normal)) => {
|
||||
let (val, _) = scope.get_mut(index);
|
||||
Some((index, AccessType::Normal)) => {
|
||||
let val = scope.get_mut(index);
|
||||
|
||||
if !val.is_shared() {
|
||||
// Replace the variable with a shared value.
|
||||
@ -2445,18 +2441,18 @@ impl Engine {
|
||||
fn calc_size(value: &Dynamic) -> (usize, usize, usize) {
|
||||
match value {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Dynamic(Union::Array(arr)) => {
|
||||
Dynamic(Union::Array(arr, _)) => {
|
||||
let mut arrays = 0;
|
||||
let mut maps = 0;
|
||||
|
||||
arr.iter().for_each(|value| match value {
|
||||
Dynamic(Union::Array(_)) => {
|
||||
Dynamic(Union::Array(_, _)) => {
|
||||
let (a, m, _) = calc_size(value);
|
||||
arrays += a;
|
||||
maps += m;
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Dynamic(Union::Map(_)) => {
|
||||
Dynamic(Union::Map(_, _)) => {
|
||||
let (a, m, _) = calc_size(value);
|
||||
arrays += a;
|
||||
maps += m;
|
||||
@ -2467,18 +2463,18 @@ impl Engine {
|
||||
(arrays, maps, 0)
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Dynamic(Union::Map(map)) => {
|
||||
Dynamic(Union::Map(map, _)) => {
|
||||
let mut arrays = 0;
|
||||
let mut maps = 0;
|
||||
|
||||
map.values().for_each(|value| match value {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Dynamic(Union::Array(_)) => {
|
||||
Dynamic(Union::Array(_, _)) => {
|
||||
let (a, m, _) = calc_size(value);
|
||||
arrays += a;
|
||||
maps += m;
|
||||
}
|
||||
Dynamic(Union::Map(_)) => {
|
||||
Dynamic(Union::Map(_, _)) => {
|
||||
let (a, m, _) = calc_size(value);
|
||||
arrays += a;
|
||||
maps += m;
|
||||
@ -2488,7 +2484,7 @@ impl Engine {
|
||||
|
||||
(arrays, maps, 0)
|
||||
}
|
||||
Dynamic(Union::Str(s)) => (0, 0, s.len()),
|
||||
Dynamic(Union::Str(s, _)) => (0, 0, s.len()),
|
||||
_ => (0, 0, 0),
|
||||
}
|
||||
}
|
||||
@ -2497,13 +2493,13 @@ impl Engine {
|
||||
// Simply return all errors
|
||||
Err(_) => return result,
|
||||
// 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
|
||||
#[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
|
||||
#[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
|
||||
Ok(_) => return result,
|
||||
};
|
||||
|
@ -8,7 +8,6 @@ use crate::engine::{
|
||||
use crate::fn_native::FnCallArgs;
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::scope::EntryType as ScopeEntryType;
|
||||
use crate::stdlib::{
|
||||
any::{type_name, TypeId},
|
||||
boxed::Box,
|
||||
@ -360,7 +359,7 @@ impl Engine {
|
||||
.map(|(name, value)| {
|
||||
let var_name: crate::stdlib::borrow::Cow<'_, str> =
|
||||
crate::r#unsafe::unsafe_cast_var_name_to_lifetime(name).into();
|
||||
(var_name, ScopeEntryType::Normal, value)
|
||||
(var_name, value)
|
||||
}),
|
||||
);
|
||||
|
||||
@ -540,15 +539,10 @@ impl Engine {
|
||||
if !func.externals.is_empty() {
|
||||
captured
|
||||
.into_iter()
|
||||
.filter(|(name, _, _, _)| func.externals.contains(name.as_ref()))
|
||||
.for_each(|(name, typ, value, _)| {
|
||||
.filter(|(name, _, _)| func.externals.contains(name.as_ref()))
|
||||
.for_each(|(name, value, _)| {
|
||||
// Consume the scope values.
|
||||
match typ {
|
||||
ScopeEntryType::Normal => scope.push(name, value),
|
||||
ScopeEntryType::Constant => {
|
||||
scope.push_constant(name, value)
|
||||
}
|
||||
};
|
||||
scope.push_dynamic(name, value);
|
||||
});
|
||||
}
|
||||
}
|
||||
@ -1000,13 +994,12 @@ impl Engine {
|
||||
.map(|expr| self.eval_expr(scope, mods, state, lib, this_ptr, expr, level))
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
let (target, _, typ, pos) =
|
||||
let (mut target, _, pos) =
|
||||
self.search_namespace(scope, mods, state, lib, this_ptr, &args_expr[0])?;
|
||||
|
||||
let target = match typ {
|
||||
ScopeEntryType::Normal => target,
|
||||
ScopeEntryType::Constant => target.into_owned(),
|
||||
};
|
||||
if target.as_ref().is_constant() {
|
||||
target = target.into_owned();
|
||||
}
|
||||
|
||||
self.inc_operations(state)
|
||||
.map_err(|err| err.fill_position(pos))?;
|
||||
@ -1087,7 +1080,7 @@ impl Engine {
|
||||
.collect::<Result<_, _>>()?;
|
||||
|
||||
// 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.inc_operations(state)
|
||||
|
@ -1640,7 +1640,7 @@ impl Module {
|
||||
// Create new module
|
||||
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
|
||||
if aliases.len() > 1 {
|
||||
aliases.into_iter().for_each(|alias| {
|
||||
|
@ -3,12 +3,11 @@
|
||||
use crate::ast::{
|
||||
BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt,
|
||||
};
|
||||
use crate::dynamic::Union;
|
||||
use crate::dynamic::{AccessType, Union};
|
||||
use crate::engine::{KEYWORD_THIS, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
|
||||
use crate::module::NamespaceRef;
|
||||
use crate::optimize::optimize_into_ast;
|
||||
use crate::optimize::OptimizationLevel;
|
||||
use crate::scope::EntryType as ScopeEntryType;
|
||||
use crate::stdlib::{
|
||||
borrow::Cow,
|
||||
boxed::Box,
|
||||
@ -49,7 +48,7 @@ struct ParseState<'e> {
|
||||
/// Interned strings.
|
||||
strings: HashMap<String, ImmutableString>,
|
||||
/// Encapsulates a local stack with variable names to simulate an actual runtime scope.
|
||||
stack: Vec<(ImmutableString, ScopeEntryType)>,
|
||||
stack: Vec<(ImmutableString, AccessType)>,
|
||||
/// Size of the local variables stack upon entry of the current block scope.
|
||||
entry_stack_len: usize,
|
||||
/// Tracks a list of external variables (variables that are not explicitly declared in the scope).
|
||||
@ -1291,11 +1290,11 @@ fn make_assignment_stmt<'a>(
|
||||
},
|
||||
) = x.as_ref();
|
||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||
ScopeEntryType::Normal => {
|
||||
AccessType::Normal => {
|
||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||
}
|
||||
// Constant values cannot be assigned to
|
||||
ScopeEntryType::Constant => {
|
||||
AccessType::Constant => {
|
||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
|
||||
}
|
||||
}
|
||||
@ -1318,11 +1317,11 @@ fn make_assignment_stmt<'a>(
|
||||
},
|
||||
) = x.as_ref();
|
||||
match state.stack[(state.stack.len() - index.unwrap().get())].1 {
|
||||
ScopeEntryType::Normal => {
|
||||
AccessType::Normal => {
|
||||
Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos))
|
||||
}
|
||||
// Constant values cannot be assigned to
|
||||
ScopeEntryType::Constant => {
|
||||
AccessType::Constant => {
|
||||
Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos))
|
||||
}
|
||||
}
|
||||
@ -1811,7 +1810,7 @@ fn parse_custom_syntax(
|
||||
// Variable searches stop at the first empty variable name.
|
||||
state.stack.resize(
|
||||
state.stack.len() + delta as usize,
|
||||
("".into(), ScopeEntryType::Normal),
|
||||
("".into(), AccessType::Normal),
|
||||
);
|
||||
}
|
||||
delta if delta < 0 && state.stack.len() <= delta.abs() as usize => state.stack.clear(),
|
||||
@ -2110,7 +2109,7 @@ fn parse_for(
|
||||
|
||||
let loop_var = state.get_interned_string(name.clone());
|
||||
let prev_stack_len = state.stack.len();
|
||||
state.stack.push((loop_var, ScopeEntryType::Normal));
|
||||
state.stack.push((loop_var, AccessType::Normal));
|
||||
|
||||
settings.is_breakable = true;
|
||||
let body = parse_block(input, state, lib, settings.level_up())?;
|
||||
@ -2125,7 +2124,7 @@ fn parse_let(
|
||||
input: &mut TokenStream,
|
||||
state: &mut ParseState,
|
||||
lib: &mut FunctionsLib,
|
||||
var_type: ScopeEntryType,
|
||||
var_type: AccessType,
|
||||
export: bool,
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Stmt, ParseError> {
|
||||
@ -2156,16 +2155,16 @@ fn parse_let(
|
||||
|
||||
match var_type {
|
||||
// let name = expr
|
||||
ScopeEntryType::Normal => {
|
||||
AccessType::Normal => {
|
||||
let var_name = state.get_interned_string(name.clone());
|
||||
state.stack.push((var_name, ScopeEntryType::Normal));
|
||||
state.stack.push((var_name, AccessType::Normal));
|
||||
let var_def = Ident::new(name, pos);
|
||||
Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos))
|
||||
}
|
||||
// const name = { expr:constant }
|
||||
ScopeEntryType::Constant => {
|
||||
AccessType::Constant => {
|
||||
let var_name = state.get_interned_string(name.clone());
|
||||
state.stack.push((var_name, ScopeEntryType::Constant));
|
||||
state.stack.push((var_name, AccessType::Constant));
|
||||
let var_def = Ident::new(name, pos);
|
||||
Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos))
|
||||
}
|
||||
@ -2232,13 +2231,13 @@ fn parse_export(
|
||||
match input.peek().unwrap() {
|
||||
(Token::Let, pos) => {
|
||||
let pos = *pos;
|
||||
let mut stmt = parse_let(input, state, lib, ScopeEntryType::Normal, true, settings)?;
|
||||
let mut stmt = parse_let(input, state, lib, AccessType::Normal, true, settings)?;
|
||||
stmt.set_position(pos);
|
||||
return Ok(stmt);
|
||||
}
|
||||
(Token::Const, pos) => {
|
||||
let pos = *pos;
|
||||
let mut stmt = parse_let(input, state, lib, ScopeEntryType::Constant, true, settings)?;
|
||||
let mut stmt = parse_let(input, state, lib, AccessType::Constant, true, settings)?;
|
||||
stmt.set_position(pos);
|
||||
return Ok(stmt);
|
||||
}
|
||||
@ -2393,7 +2392,7 @@ fn parse_stmt(
|
||||
lib: &mut FunctionsLib,
|
||||
mut settings: ParseSettings,
|
||||
) -> Result<Option<Stmt>, ParseError> {
|
||||
use ScopeEntryType::{Constant, Normal};
|
||||
use AccessType::{Constant, Normal};
|
||||
|
||||
let (token, token_pos) = match input.peek().unwrap() {
|
||||
(Token::EOF, pos) => return Ok(Some(Stmt::Noop(*pos))),
|
||||
@ -2637,7 +2636,7 @@ fn parse_fn(
|
||||
return Err(PERR::FnDuplicatedParam(name, s).into_err(pos));
|
||||
}
|
||||
let s = state.get_interned_string(s);
|
||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||
state.stack.push((s.clone(), AccessType::Normal));
|
||||
params.push((s, pos))
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
@ -2770,7 +2769,7 @@ fn parse_anon_fn(
|
||||
return Err(PERR::FnDuplicatedParam("".to_string(), s).into_err(pos));
|
||||
}
|
||||
let s = state.get_interned_string(s);
|
||||
state.stack.push((s.clone(), ScopeEntryType::Normal));
|
||||
state.stack.push((s.clone(), AccessType::Normal));
|
||||
params.push((s, pos))
|
||||
}
|
||||
(Token::LexError(err), pos) => return Err(err.into_err(pos)),
|
||||
@ -3005,15 +3004,15 @@ impl Engine {
|
||||
pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
||||
match value.0 {
|
||||
#[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::Int(value) => Some(Expr::IntegerConstant(value, pos)),
|
||||
Union::Char(value) => Some(Expr::CharConstant(value, pos)),
|
||||
Union::Str(value) => Some(Expr::StringConstant(value, pos)),
|
||||
Union::Bool(value) => Some(Expr::BoolConstant(value, pos)),
|
||||
Union::Unit(_, _) => Some(Expr::Unit(pos)),
|
||||
Union::Int(value, _) => Some(Expr::IntegerConstant(value, pos)),
|
||||
Union::Char(value, _) => Some(Expr::CharConstant(value, pos)),
|
||||
Union::Str(value, _) => Some(Expr::StringConstant(value, pos)),
|
||||
Union::Bool(value, _) => Some(Expr::BoolConstant(value, pos)),
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Union::Array(array) => {
|
||||
Union::Array(array, _) => {
|
||||
let items: Vec<_> = array
|
||||
.into_iter()
|
||||
.map(|x| map_dynamic_to_expr(x, pos))
|
||||
@ -3029,7 +3028,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option<Expr> {
|
||||
}
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Union::Map(map) => {
|
||||
Union::Map(map, _) => {
|
||||
let items: Vec<_> = map
|
||||
.into_iter()
|
||||
.map(|(k, v)| (IdentX::new(k, pos), map_dynamic_to_expr(v, pos)))
|
||||
|
80
src/scope.rs
80
src/scope.rs
@ -1,29 +1,9 @@
|
||||
//! Module that defines the [`Scope`] type representing a function call-stack scope.
|
||||
|
||||
use crate::dynamic::Variant;
|
||||
use crate::dynamic::{AccessType, Variant};
|
||||
use crate::stdlib::{borrow::Cow, boxed::Box, iter, string::String, vec::Vec};
|
||||
use crate::{Dynamic, 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.
|
||||
/// Useful for keeping state between [`Engine`][crate::Engine] evaluation runs.
|
||||
///
|
||||
@ -58,19 +38,17 @@ impl EntryType {
|
||||
//
|
||||
// # 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,
|
||||
// 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.
|
||||
//
|
||||
// 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.
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug)]
|
||||
pub struct Scope<'a> {
|
||||
/// Current value of the entry.
|
||||
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.
|
||||
names: Vec<(Cow<'a, str>, Box<StaticVec<String>>)>,
|
||||
}
|
||||
@ -79,7 +57,6 @@ impl Default for Scope<'_> {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
values: Vec::with_capacity(16),
|
||||
types: Vec::with_capacity(16),
|
||||
names: Vec::with_capacity(16),
|
||||
}
|
||||
}
|
||||
@ -124,7 +101,6 @@ impl<'a> Scope<'a> {
|
||||
#[inline(always)]
|
||||
pub fn clear(&mut self) -> &mut Self {
|
||||
self.names.clear();
|
||||
self.types.clear();
|
||||
self.values.clear();
|
||||
self
|
||||
}
|
||||
@ -180,7 +156,7 @@ impl<'a> Scope<'a> {
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Normal, Dynamic::from(value))
|
||||
self.push_dynamic_value(name, AccessType::Normal, Dynamic::from(value))
|
||||
}
|
||||
/// Add (push) a new [`Dynamic`] entry to the [`Scope`].
|
||||
///
|
||||
@ -196,7 +172,7 @@ impl<'a> Scope<'a> {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
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_type(), value)
|
||||
}
|
||||
/// Add (push) a new constant to the [`Scope`].
|
||||
///
|
||||
@ -219,7 +195,7 @@ impl<'a> Scope<'a> {
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
value: impl Variant + Clone,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Constant, Dynamic::from(value))
|
||||
self.push_dynamic_value(name, AccessType::Constant, Dynamic::from(value))
|
||||
}
|
||||
/// 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>>,
|
||||
value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.push_dynamic_value(name, EntryType::Constant, value)
|
||||
self.push_dynamic_value(name, AccessType::Constant, value)
|
||||
}
|
||||
/// Add (push) a new entry with a [`Dynamic`] value to the [`Scope`].
|
||||
#[inline]
|
||||
pub(crate) fn push_dynamic_value(
|
||||
&mut self,
|
||||
name: impl Into<Cow<'a, str>>,
|
||||
entry_type: EntryType,
|
||||
value: Dynamic,
|
||||
access: AccessType,
|
||||
mut value: Dynamic,
|
||||
) -> &mut Self {
|
||||
self.names.push((name.into(), Box::new(Default::default())));
|
||||
self.types.push(entry_type);
|
||||
value.set_access_type(access);
|
||||
self.values.push(value.into());
|
||||
self
|
||||
}
|
||||
@ -286,7 +262,6 @@ impl<'a> Scope<'a> {
|
||||
#[inline(always)]
|
||||
pub fn rewind(&mut self, size: usize) -> &mut Self {
|
||||
self.names.truncate(size);
|
||||
self.types.truncate(size);
|
||||
self.values.truncate(size);
|
||||
self
|
||||
}
|
||||
@ -312,14 +287,14 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
/// Find an entry in the [`Scope`], starting from the last.
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, EntryType)> {
|
||||
pub(crate) fn get_index(&self, name: &str) -> Option<(usize, AccessType)> {
|
||||
self.names
|
||||
.iter()
|
||||
.enumerate()
|
||||
.rev() // Always search a Scope in reverse order
|
||||
.find_map(|(index, (key, _))| {
|
||||
if name == key.as_ref() {
|
||||
Some((index, self.types[index]))
|
||||
Some((index, self.values[index].access_type()))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
@ -374,8 +349,8 @@ impl<'a> Scope<'a> {
|
||||
None => {
|
||||
self.push(name, value);
|
||||
}
|
||||
Some((_, EntryType::Constant)) => panic!("variable {} is constant", name),
|
||||
Some((index, EntryType::Normal)) => {
|
||||
Some((_, AccessType::Constant)) => panic!("variable {} is constant", name),
|
||||
Some((index, AccessType::Normal)) => {
|
||||
*self.values.get_mut(index).unwrap() = Dynamic::from(value);
|
||||
}
|
||||
}
|
||||
@ -383,11 +358,8 @@ impl<'a> Scope<'a> {
|
||||
}
|
||||
/// Get a mutable reference to an entry in the [`Scope`].
|
||||
#[inline(always)]
|
||||
pub(crate) fn get_mut(&mut self, index: usize) -> (&mut Dynamic, EntryType) {
|
||||
(
|
||||
self.values.get_mut(index).expect("invalid index in Scope"),
|
||||
self.types[index],
|
||||
)
|
||||
pub(crate) fn get_mut(&mut self, index: usize) -> &mut Dynamic {
|
||||
self.values.get_mut(index).expect("invalid index in Scope")
|
||||
}
|
||||
/// Update the access type of an entry in the [`Scope`].
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
@ -412,7 +384,6 @@ impl<'a> Scope<'a> {
|
||||
.for_each(|(index, (name, alias))| {
|
||||
if !entries.names.iter().any(|(key, _)| key == name) {
|
||||
entries.names.push((name.clone(), alias.clone()));
|
||||
entries.types.push(self.types[index]);
|
||||
entries.values.push(self.values[index].clone());
|
||||
}
|
||||
});
|
||||
@ -422,13 +393,11 @@ impl<'a> Scope<'a> {
|
||||
/// Get an iterator to entries in the [`Scope`].
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn into_iter(
|
||||
self,
|
||||
) -> impl Iterator<Item = (Cow<'a, str>, EntryType, Dynamic, Vec<String>)> {
|
||||
pub(crate) fn into_iter(self) -> impl Iterator<Item = (Cow<'a, str>, Dynamic, Vec<String>)> {
|
||||
self.names
|
||||
.into_iter()
|
||||
.zip(self.types.into_iter().zip(self.values.into_iter()))
|
||||
.map(|((name, alias), (typ, value))| (name, typ, value, alias.to_vec()))
|
||||
.zip(self.values.into_iter())
|
||||
.map(|((name, alias), value)| (name, value, alias.to_vec()))
|
||||
}
|
||||
/// Get an iterator to entries in the [`Scope`].
|
||||
/// Shared values are flatten-cloned.
|
||||
@ -466,17 +435,16 @@ impl<'a> Scope<'a> {
|
||||
pub fn iter_raw<'x: 'a>(&'x self) -> impl Iterator<Item = (&'a str, bool, &'x Dynamic)> + 'x {
|
||||
self.names
|
||||
.iter()
|
||||
.zip(self.types.iter().zip(self.values.iter()))
|
||||
.map(|((name, _), (typ, value))| (name.as_ref(), typ.is_constant(), value))
|
||||
.zip(self.values.iter())
|
||||
.map(|((name, _), value)| (name.as_ref(), value.is_constant(), 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)]
|
||||
fn extend<T: IntoIterator<Item = (K, EntryType, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, typ, value)| {
|
||||
fn extend<T: IntoIterator<Item = (K, Dynamic)>>(&mut self, iter: T) {
|
||||
iter.into_iter().for_each(|(name, value)| {
|
||||
self.names.push((name.into(), Box::new(Default::default())));
|
||||
self.types.push(typ);
|
||||
self.values.push(value);
|
||||
});
|
||||
}
|
||||
|
@ -178,7 +178,10 @@ fn test_arrays_map_reduce() -> Result<(), Box<EvalAltResult>> {
|
||||
engine.eval::<INT>(
|
||||
r#"
|
||||
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
|
||||
|
Loading…
Reference in New Issue
Block a user