Indexer as fallback to property.
This commit is contained in:
parent
e64dad4e9f
commit
dc9b4d7f4d
@ -15,6 +15,8 @@ Breaking changes
|
|||||||
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
* `Engine::disable_doc_comments` is removed because doc-comments are now placed under the `metadata` feature flag.
|
||||||
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
* Registering a custom syntax now only requires specifying whether the `Scope` is adjusted (i.e. whether variables are added or removed). There is no need to specify the number of variables added/removed.
|
||||||
* Assigning to a property of a constant is now allowed and no longer raise an `EvalAltResult::ErrorAssignmentToConstant` error. This is to facilitate the Singleton pattern. Registered setter functions are automatically guarded against setters calling on constants and will continue to raise errors unless the `pure` attribute is present (for plugins).
|
* Assigning to a property of a constant is now allowed and no longer raise an `EvalAltResult::ErrorAssignmentToConstant` error. This is to facilitate the Singleton pattern. Registered setter functions are automatically guarded against setters calling on constants and will continue to raise errors unless the `pure` attribute is present (for plugins).
|
||||||
|
* If a property getter/setter is not found, an indexer with string index, if any, is tried.
|
||||||
|
* The indexers API (`Engine::register_indexer_XXX` and `Module::set_indexer_XXX`) are now also exposed under `no_index`.
|
||||||
|
|
||||||
New features
|
New features
|
||||||
------------
|
------------
|
||||||
@ -23,6 +25,7 @@ New features
|
|||||||
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
|
* A new internal feature `no_smartstring` to turn off `SmartString` for those rare cases that it is needed.
|
||||||
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
|
* `DynamicReadLock` and `DynamicWriteLoc` are exposed under `internals`.
|
||||||
* `From<Shared<Locked<Dynamic>>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
|
* `From<Shared<Locked<Dynamic>>>` is added for `Dynamic` mapping directly to a shared value, together with support for `Dynamic::from`.
|
||||||
|
* An indexer with string index acts as a _fallback_ to a property getter/setter.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
14
src/ast.rs
14
src/ast.rs
@ -1715,7 +1715,13 @@ pub enum Expr {
|
|||||||
)>,
|
)>,
|
||||||
),
|
),
|
||||||
/// Property access - ((getter, hash), (setter, hash), prop)
|
/// Property access - ((getter, hash), (setter, hash), prop)
|
||||||
Property(Box<((Identifier, u64), (Identifier, u64), Ident)>),
|
Property(
|
||||||
|
Box<(
|
||||||
|
(Identifier, u64),
|
||||||
|
(Identifier, u64),
|
||||||
|
(ImmutableString, Position),
|
||||||
|
)>,
|
||||||
|
),
|
||||||
/// { [statement][Stmt] ... }
|
/// { [statement][Stmt] ... }
|
||||||
Stmt(Box<StmtBlock>),
|
Stmt(Box<StmtBlock>),
|
||||||
/// func `(` expr `,` ... `)`
|
/// func `(` expr `,` ... `)`
|
||||||
@ -1780,7 +1786,7 @@ impl fmt::Debug for Expr {
|
|||||||
}
|
}
|
||||||
f.write_str(")")
|
f.write_str(")")
|
||||||
}
|
}
|
||||||
Self::Property(x) => write!(f, "Property({})", x.2.name),
|
Self::Property(x) => write!(f, "Property({})", (x.2).0),
|
||||||
Self::Stmt(x) => {
|
Self::Stmt(x) => {
|
||||||
f.write_str("Stmt")?;
|
f.write_str("Stmt")?;
|
||||||
f.debug_list().entries(x.0.iter()).finish()
|
f.debug_list().entries(x.0.iter()).finish()
|
||||||
@ -1896,7 +1902,7 @@ impl Expr {
|
|||||||
|
|
||||||
Self::InterpolatedString(x) => x.first().unwrap().position(),
|
Self::InterpolatedString(x) => x.first().unwrap().position(),
|
||||||
|
|
||||||
Self::Property(x) => (x.2).pos,
|
Self::Property(x) => (x.2).1,
|
||||||
Self::Stmt(x) => x.1,
|
Self::Stmt(x) => x.1,
|
||||||
|
|
||||||
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
|
Self::And(x, _) | Self::Or(x, _) | Self::Dot(x, _) | Self::Index(x, _) => {
|
||||||
@ -1931,7 +1937,7 @@ impl Expr {
|
|||||||
x.first_mut().unwrap().set_position(new_pos);
|
x.first_mut().unwrap().set_position(new_pos);
|
||||||
}
|
}
|
||||||
|
|
||||||
Self::Property(x) => (x.2).pos = new_pos,
|
Self::Property(x) => (x.2).1 = new_pos,
|
||||||
Self::Stmt(x) => x.1 = new_pos,
|
Self::Stmt(x) => x.1 = new_pos,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
137
src/engine.rs
137
src/engine.rs
@ -224,9 +224,9 @@ pub const KEYWORD_GLOBAL: &str = "global";
|
|||||||
pub const FN_GET: &str = "get$";
|
pub const FN_GET: &str = "get$";
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
pub const FN_SET: &str = "set$";
|
pub const FN_SET: &str = "set$";
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
pub const FN_IDX_GET: &str = "index$get$";
|
pub const FN_IDX_GET: &str = "index$get$";
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
pub const FN_IDX_SET: &str = "index$set$";
|
pub const FN_IDX_SET: &str = "index$set$";
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub const FN_ANONYMOUS: &str = "anon$";
|
pub const FN_ANONYMOUS: &str = "anon$";
|
||||||
@ -1256,7 +1256,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id op= ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
|
||||||
let Ident { name, pos, .. } = &x.2;
|
let (name, pos) = &x.2;
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target, index, *pos, true, is_ref, false, level,
|
mods, state, lib, target, index, *pos, true, is_ref, false, level,
|
||||||
@ -1269,7 +1269,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target.is::<Map>() => {
|
Expr::Property(x) if target.is::<Map>() => {
|
||||||
let Ident { name, pos, .. } = &x.2;
|
let (name, pos) = &x.2;
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
let val = self.get_indexed_mut(
|
let val = self.get_indexed_mut(
|
||||||
mods, state, lib, target, index, *pos, false, is_ref, false, level,
|
mods, state, lib, target, index, *pos, false, is_ref, false, level,
|
||||||
@ -1279,8 +1279,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
// xxx.id op= ???
|
// xxx.id op= ???
|
||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
|
||||||
x.as_ref();
|
|
||||||
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||||
|
|
||||||
if op_info.is_some() {
|
if op_info.is_some() {
|
||||||
@ -1303,24 +1302,66 @@ impl Engine {
|
|||||||
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, true))
|
.or_else(|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let mut prop = name.into();
|
||||||
|
let args = &mut [target, &mut prop, &mut new_val];
|
||||||
|
let hash_set = FnCallHashes::from_native(crate::calc_fn_hash(
|
||||||
|
std::iter::empty(),
|
||||||
|
FN_IDX_SET,
|
||||||
|
3,
|
||||||
|
));
|
||||||
|
self.exec_fn_call(
|
||||||
|
mods, state, lib, FN_IDX_SET, hash_set, args, is_ref, true,
|
||||||
|
*pos, None, level,
|
||||||
|
)
|
||||||
|
.map_err(
|
||||||
|
|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(x) => {
|
Expr::Property(x) => {
|
||||||
let ((getter, hash_get), _, Ident { pos, .. }) = x.as_ref();
|
let ((getter, hash_get), _, (name, pos)) = x.as_ref();
|
||||||
let hash = FnCallHashes::from_native(*hash_get);
|
let hash = FnCallHashes::from_native(*hash_get);
|
||||||
let mut args = [target.as_mut()];
|
let mut args = [target.as_mut()];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
||||||
level,
|
level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| (v, false))
|
.map_or_else(
|
||||||
|
|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let prop = name.into();
|
||||||
|
self.get_indexed_mut(
|
||||||
|
mods, state, lib, target, prop, *pos, false, is_ref, true,
|
||||||
|
level,
|
||||||
|
)
|
||||||
|
.map(|v| (v.take_or_clone(), false))
|
||||||
|
.map_err(|idx_err| {
|
||||||
|
match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
},
|
||||||
|
|(v, _)| Ok((v, false)),
|
||||||
|
)
|
||||||
}
|
}
|
||||||
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
||||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
||||||
let mut val = match &x.lhs {
|
let mut val = match &x.lhs {
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let Ident { name, pos, .. } = &p.2;
|
let (name, pos) = &p.2;
|
||||||
let index = name.into();
|
let index = name.into();
|
||||||
self.get_indexed_mut(
|
self.get_indexed_mut(
|
||||||
mods, state, lib, target, index, *pos, false, is_ref, true,
|
mods, state, lib, target, index, *pos, false, is_ref, true,
|
||||||
@ -1356,17 +1397,36 @@ impl Engine {
|
|||||||
match &x.lhs {
|
match &x.lhs {
|
||||||
// xxx.prop[expr] | xxx.prop.expr
|
// xxx.prop[expr] | xxx.prop.expr
|
||||||
Expr::Property(p) => {
|
Expr::Property(p) => {
|
||||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
||||||
p.as_ref();
|
p.as_ref();
|
||||||
let rhs_chain = rhs_chain.unwrap();
|
let rhs_chain = rhs_chain.unwrap();
|
||||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||||
let hash_set = FnCallHashes::from_native(*hash_set);
|
let hash_set = FnCallHashes::from_native(*hash_set);
|
||||||
let arg_values = &mut [target.as_mut(), &mut Default::default()];
|
let mut arg_values = [target.as_mut(), &mut Default::default()];
|
||||||
let args = &mut arg_values[..1];
|
let args = &mut arg_values[..1];
|
||||||
let (mut val, updated) = self.exec_fn_call(
|
let (mut val, updated) = self
|
||||||
mods, state, lib, getter, hash_get, args, is_ref, true, *pos,
|
.exec_fn_call(
|
||||||
None, level,
|
mods, state, lib, getter, hash_get, args, is_ref, true,
|
||||||
)?;
|
*pos, None, level,
|
||||||
|
)
|
||||||
|
.or_else(|err| match *err {
|
||||||
|
// Try an indexer if property does not exist
|
||||||
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
|
let prop = name.into();
|
||||||
|
self.get_indexed_mut(
|
||||||
|
mods, state, lib, target, prop, *pos, false,
|
||||||
|
is_ref, true, level,
|
||||||
|
)
|
||||||
|
.map(|v| (v.take_or_clone(), false))
|
||||||
|
.map_err(
|
||||||
|
|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => err,
|
||||||
|
_ => idx_err,
|
||||||
|
},
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => Err(err),
|
||||||
|
})?;
|
||||||
|
|
||||||
let val = &mut val;
|
let val = &mut val;
|
||||||
|
|
||||||
@ -1389,17 +1449,36 @@ impl Engine {
|
|||||||
// Feed the value back via a setter just in case it has been updated
|
// Feed the value back via a setter just in case it has been updated
|
||||||
if updated || may_be_changed {
|
if updated || may_be_changed {
|
||||||
// Re-use args because the first &mut parameter will not be consumed
|
// Re-use args because the first &mut parameter will not be consumed
|
||||||
arg_values[1] = val;
|
let mut arg_values = [target.as_mut(), val];
|
||||||
|
let args = &mut arg_values;
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, hash_set, arg_values, is_ref,
|
mods, state, lib, setter, hash_set, args, is_ref, true,
|
||||||
true, *pos, None, level,
|
*pos, None, level,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
// If there is no setter, no need to feed it back because
|
// Try an indexer if property does not exist
|
||||||
// the property is read-only
|
|
||||||
EvalAltResult::ErrorDotExpr(_, _) => {
|
EvalAltResult::ErrorDotExpr(_, _) => {
|
||||||
Ok((Dynamic::UNIT, false))
|
let mut prop = name.into();
|
||||||
|
let args = &mut [target.as_mut(), &mut prop, val];
|
||||||
|
let hash_set =
|
||||||
|
FnCallHashes::from_native(crate::calc_fn_hash(
|
||||||
|
std::iter::empty(),
|
||||||
|
FN_IDX_SET,
|
||||||
|
3,
|
||||||
|
));
|
||||||
|
self.exec_fn_call(
|
||||||
|
mods, state, lib, FN_IDX_SET, hash_set, args,
|
||||||
|
is_ref, true, *pos, None, level,
|
||||||
|
)
|
||||||
|
.or_else(|idx_err| match *idx_err {
|
||||||
|
EvalAltResult::ErrorIndexingType(_, _) => {
|
||||||
|
// If there is no setter, no need to feed it back because
|
||||||
|
// the property is read-only
|
||||||
|
Ok((Dynamic::UNIT, false))
|
||||||
|
}
|
||||||
|
_ => Err(idx_err),
|
||||||
|
})
|
||||||
}
|
}
|
||||||
_ => Err(err),
|
_ => Err(err),
|
||||||
},
|
},
|
||||||
@ -1550,7 +1629,7 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||||
idx_values.push(ChainArgument::Property(x.2.pos))
|
idx_values.push(ChainArgument::Property((x.2).1))
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
@ -1561,7 +1640,7 @@ impl Engine {
|
|||||||
let lhs_val = match lhs {
|
let lhs_val = match lhs {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||||
ChainArgument::Property(x.2.pos)
|
ChainArgument::Property((x.2).1)
|
||||||
}
|
}
|
||||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||||
|
|
||||||
@ -1748,7 +1827,6 @@ impl Engine {
|
|||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if _indexers => {
|
_ if _indexers => {
|
||||||
let type_name = target.type_name();
|
|
||||||
let args = &mut [target, &mut _idx];
|
let args = &mut [target, &mut _idx];
|
||||||
let hash_get =
|
let hash_get =
|
||||||
FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2));
|
FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2));
|
||||||
@ -1758,15 +1836,6 @@ impl Engine {
|
|||||||
_level,
|
_level,
|
||||||
)
|
)
|
||||||
.map(|(v, _)| v.into())
|
.map(|(v, _)| v.into())
|
||||||
.map_err(|err| match *err {
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _) if fn_sig.ends_with(']') => {
|
|
||||||
Box::new(EvalAltResult::ErrorIndexingType(
|
|
||||||
type_name.into(),
|
|
||||||
Position::NONE,
|
|
||||||
))
|
|
||||||
}
|
|
||||||
_ => err,
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_ => EvalAltResult::ErrorIndexingType(
|
_ => EvalAltResult::ErrorIndexingType(
|
||||||
|
@ -534,7 +534,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -574,12 +574,13 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -600,7 +601,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The function signature must start with `&mut self` and not `&self`.
|
/// The function signature must start with `&mut self` and not `&self`.
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -642,7 +643,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_result<
|
pub fn register_indexer_get_result<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
@ -652,6 +653,7 @@ impl Engine {
|
|||||||
&mut self,
|
&mut self,
|
||||||
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -670,7 +672,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Register an index setter for a custom type with the [`Engine`].
|
/// Register an index setter for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -712,12 +714,13 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -736,7 +739,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Register an index setter for a custom type with the [`Engine`].
|
/// Register an index setter for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -781,7 +784,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_set_result<
|
pub fn register_indexer_set_result<
|
||||||
T: Variant + Clone,
|
T: Variant + Clone,
|
||||||
@ -791,6 +794,7 @@ impl Engine {
|
|||||||
&mut self,
|
&mut self,
|
||||||
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -809,7 +813,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`].
|
/// Short-hand for registering both index getter and setter functions for a custom type with the [`Engine`].
|
||||||
///
|
///
|
||||||
/// Not available under `no_index`.
|
/// Not available under both `no_index` and `no_object`.
|
||||||
///
|
///
|
||||||
/// # Panics
|
/// # Panics
|
||||||
///
|
///
|
||||||
@ -851,7 +855,7 @@ impl Engine {
|
|||||||
/// # Ok(())
|
/// # Ok(())
|
||||||
/// # }
|
/// # }
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -375,12 +375,8 @@ impl Engine {
|
|||||||
crate::engine::FN_IDX_GET => {
|
crate::engine::FN_IDX_GET => {
|
||||||
assert!(args.len() == 2);
|
assert!(args.len() == 2);
|
||||||
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(
|
EvalAltResult::ErrorIndexingType(
|
||||||
format!(
|
self.map_type_name(args[0].type_name()).to_string(),
|
||||||
"{} [{}]",
|
|
||||||
self.map_type_name(args[0].type_name()),
|
|
||||||
self.map_type_name(args[1].type_name()),
|
|
||||||
),
|
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
@ -391,12 +387,8 @@ impl Engine {
|
|||||||
crate::engine::FN_IDX_SET => {
|
crate::engine::FN_IDX_SET => {
|
||||||
assert!(args.len() == 3);
|
assert!(args.len() == 3);
|
||||||
|
|
||||||
EvalAltResult::ErrorFunctionNotFound(
|
EvalAltResult::ErrorIndexingType(
|
||||||
format!(
|
self.map_type_name(args[0].type_name()).to_string(),
|
||||||
"{} [{}]=",
|
|
||||||
self.map_type_name(args[0].type_name()),
|
|
||||||
self.map_type_name(args[1].type_name()),
|
|
||||||
),
|
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.into()
|
.into()
|
||||||
|
@ -24,7 +24,6 @@ use std::{
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
use crate::Array;
|
use crate::Array;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
use crate::Map;
|
use crate::Map;
|
||||||
|
|
||||||
@ -956,7 +955,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
|
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
|
||||||
where
|
where
|
||||||
@ -966,6 +965,7 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||||
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -1016,7 +1016,7 @@ impl Module {
|
|||||||
/// });
|
/// });
|
||||||
/// assert!(module.contains_fn(hash));
|
/// assert!(module.contains_fn(hash));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
|
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
|
||||||
where
|
where
|
||||||
@ -1026,6 +1026,7 @@ impl Module {
|
|||||||
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
||||||
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||||
{
|
{
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||||
panic!("Cannot register indexer for arrays.");
|
panic!("Cannot register indexer for arrays.");
|
||||||
}
|
}
|
||||||
@ -1082,7 +1083,7 @@ impl Module {
|
|||||||
/// assert!(module.contains_fn(hash_get));
|
/// assert!(module.contains_fn(hash_get));
|
||||||
/// assert!(module.contains_fn(hash_set));
|
/// assert!(module.contains_fn(hash_set));
|
||||||
/// ```
|
/// ```
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn set_indexer_get_set_fn<A, B, T>(
|
pub fn set_indexer_get_set_fn<A, B, T>(
|
||||||
&mut self,
|
&mut self,
|
||||||
|
@ -696,7 +696,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) {
|
|||||||
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
Expr::Dot(x, _) => match (&mut x.lhs, &mut x.rhs) {
|
||||||
// map.string
|
// map.string
|
||||||
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
(Expr::Map(m, pos), Expr::Property(p)) if m.0.iter().all(|(_, x)| x.is_pure()) => {
|
||||||
let prop = &p.2.name;
|
let prop = p.2.0.as_str();
|
||||||
// Map literal where everything is pure - promote the indexed item.
|
// Map literal where everything is pure - promote the indexed item.
|
||||||
// All other items can be thrown away.
|
// All other items can be thrown away.
|
||||||
state.set_dirty();
|
state.set_dirty();
|
||||||
|
@ -232,10 +232,7 @@ impl Expr {
|
|||||||
Self::Property(Box::new((
|
Self::Property(Box::new((
|
||||||
(getter, hash_get),
|
(getter, hash_get),
|
||||||
(setter, hash_set),
|
(setter, hash_set),
|
||||||
Ident {
|
(state.get_identifier(ident).into(), pos),
|
||||||
name: state.get_identifier(ident),
|
|
||||||
pos,
|
|
||||||
},
|
|
||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
_ => self,
|
_ => self,
|
||||||
@ -1541,10 +1538,7 @@ fn make_dot_expr(
|
|||||||
let rhs = Expr::Property(Box::new((
|
let rhs = Expr::Property(Box::new((
|
||||||
(getter, hash_get),
|
(getter, hash_get),
|
||||||
(setter, hash_set),
|
(setter, hash_set),
|
||||||
Ident {
|
(state.get_identifier(ident).into(), var_pos),
|
||||||
name: state.get_identifier(ident),
|
|
||||||
pos: var_pos,
|
|
||||||
},
|
|
||||||
)));
|
)));
|
||||||
|
|
||||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
#![cfg(not(feature = "no_object"))]
|
#![cfg(not(feature = "no_object"))]
|
||||||
|
|
||||||
use rhai::{Engine, EvalAltResult, ImmutableString, INT};
|
use rhai::{Engine, EvalAltResult, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -46,20 +46,27 @@ fn test_get_set() -> Result<(), Box<EvalAltResult>> {
|
|||||||
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.x.add(); a.x")?, 42);
|
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.x.add(); a.x")?, 42);
|
||||||
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.y.add(); a.y")?, 0);
|
assert_eq!(engine.eval::<INT>("let a = new_ts(); a.y.add(); a.y")?, 0);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
{
|
{
|
||||||
engine.register_indexer_get_set(
|
engine.register_indexer_get_set(
|
||||||
|value: &mut TestStruct, index: ImmutableString| value.array[index.len()],
|
|value: &mut TestStruct, index: &str| value.array[index.len()],
|
||||||
|value: &mut TestStruct, index: ImmutableString, new_val: INT| {
|
|value: &mut TestStruct, index: &str, new_val: INT| value.array[index.len()] = new_val,
|
||||||
value.array[index.len()] = new_val
|
|
||||||
},
|
|
||||||
);
|
);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(engine.eval::<INT>(r#"let a = new_ts(); a["abc"]"#)?, 4);
|
assert_eq!(engine.eval::<INT>(r#"let a = new_ts(); a["abc"]"#)?, 4);
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
engine.eval::<INT>(r#"let a = new_ts(); a["abc"] = 42; a["abc"]"#)?,
|
engine.eval::<INT>(r#"let a = new_ts(); a["abc"] = 42; a["abc"]"#)?,
|
||||||
42
|
42
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>(r"let a = new_ts(); a.abc")?, 4);
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(r"let a = new_ts(); a.abc = 42; a.abc")?,
|
||||||
|
42
|
||||||
|
);
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user