Indexer as fallback to property.
This commit is contained in:
14
src/ast.rs
14
src/ast.rs
@@ -1715,7 +1715,13 @@ pub enum Expr {
|
||||
)>,
|
||||
),
|
||||
/// Property access - ((getter, hash), (setter, hash), prop)
|
||||
Property(Box<((Identifier, u64), (Identifier, u64), Ident)>),
|
||||
Property(
|
||||
Box<(
|
||||
(Identifier, u64),
|
||||
(Identifier, u64),
|
||||
(ImmutableString, Position),
|
||||
)>,
|
||||
),
|
||||
/// { [statement][Stmt] ... }
|
||||
Stmt(Box<StmtBlock>),
|
||||
/// func `(` expr `,` ... `)`
|
||||
@@ -1780,7 +1786,7 @@ impl fmt::Debug for Expr {
|
||||
}
|
||||
f.write_str(")")
|
||||
}
|
||||
Self::Property(x) => write!(f, "Property({})", x.2.name),
|
||||
Self::Property(x) => write!(f, "Property({})", (x.2).0),
|
||||
Self::Stmt(x) => {
|
||||
f.write_str("Stmt")?;
|
||||
f.debug_list().entries(x.0.iter()).finish()
|
||||
@@ -1896,7 +1902,7 @@ impl Expr {
|
||||
|
||||
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::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);
|
||||
}
|
||||
|
||||
Self::Property(x) => (x.2).pos = new_pos,
|
||||
Self::Property(x) => (x.2).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$";
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
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$";
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
pub const FN_IDX_SET: &str = "index$set$";
|
||||
#[cfg(not(feature = "no_function"))]
|
||||
pub const FN_ANONYMOUS: &str = "anon$";
|
||||
@@ -1256,7 +1256,7 @@ impl Engine {
|
||||
}
|
||||
// {xxx:map}.id op= ???
|
||||
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 val = self.get_indexed_mut(
|
||||
mods, state, lib, target, index, *pos, true, is_ref, false, level,
|
||||
@@ -1269,7 +1269,7 @@ impl Engine {
|
||||
}
|
||||
// {xxx:map}.id
|
||||
Expr::Property(x) if target.is::<Map>() => {
|
||||
let Ident { name, pos, .. } = &x.2;
|
||||
let (name, pos) = &x.2;
|
||||
let index = name.into();
|
||||
let val = self.get_indexed_mut(
|
||||
mods, state, lib, target, index, *pos, false, is_ref, false, level,
|
||||
@@ -1279,8 +1279,7 @@ impl Engine {
|
||||
}
|
||||
// xxx.id op= ???
|
||||
Expr::Property(x) if new_val.is_some() => {
|
||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
||||
x.as_ref();
|
||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) = x.as_ref();
|
||||
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.unwrap();
|
||||
|
||||
if op_info.is_some() {
|
||||
@@ -1303,24 +1302,66 @@ impl Engine {
|
||||
mods, state, lib, setter, hash, &mut args, is_ref, true, *pos, None,
|
||||
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
|
||||
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 mut args = [target.as_mut()];
|
||||
self.exec_fn_call(
|
||||
mods, state, lib, getter, hash, &mut args, is_ref, true, *pos, None,
|
||||
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
|
||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target.is::<Map>() => {
|
||||
let mut val = match &x.lhs {
|
||||
Expr::Property(p) => {
|
||||
let Ident { name, pos, .. } = &p.2;
|
||||
let (name, pos) = &p.2;
|
||||
let index = name.into();
|
||||
self.get_indexed_mut(
|
||||
mods, state, lib, target, index, *pos, false, is_ref, true,
|
||||
@@ -1356,17 +1397,36 @@ impl Engine {
|
||||
match &x.lhs {
|
||||
// xxx.prop[expr] | xxx.prop.expr
|
||||
Expr::Property(p) => {
|
||||
let ((getter, hash_get), (setter, hash_set), Ident { pos, .. }) =
|
||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
||||
p.as_ref();
|
||||
let rhs_chain = rhs_chain.unwrap();
|
||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||
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 (mut val, updated) = self.exec_fn_call(
|
||||
mods, state, lib, getter, hash_get, args, is_ref, true, *pos,
|
||||
None, level,
|
||||
)?;
|
||||
let (mut val, updated) = self
|
||||
.exec_fn_call(
|
||||
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;
|
||||
|
||||
@@ -1389,17 +1449,36 @@ impl Engine {
|
||||
// Feed the value back via a setter just in case it has been updated
|
||||
if updated || may_be_changed {
|
||||
// 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(
|
||||
mods, state, lib, setter, hash_set, arg_values, is_ref,
|
||||
true, *pos, None, level,
|
||||
mods, state, lib, setter, hash_set, args, is_ref, true,
|
||||
*pos, None, level,
|
||||
)
|
||||
.or_else(
|
||||
|err| match *err {
|
||||
// If there is no setter, no need to feed it back because
|
||||
// the property is read-only
|
||||
// Try an indexer if property does not exist
|
||||
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),
|
||||
},
|
||||
@@ -1550,7 +1629,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
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"),
|
||||
|
||||
@@ -1561,7 +1640,7 @@ impl Engine {
|
||||
let lhs_val = match lhs {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
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"),
|
||||
|
||||
@@ -1748,7 +1827,6 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if _indexers => {
|
||||
let type_name = target.type_name();
|
||||
let args = &mut [target, &mut _idx];
|
||||
let hash_get =
|
||||
FnCallHashes::from_native(calc_fn_hash(std::iter::empty(), FN_IDX_GET, 2));
|
||||
@@ -1758,15 +1836,6 @@ impl Engine {
|
||||
_level,
|
||||
)
|
||||
.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(
|
||||
|
@@ -534,7 +534,7 @@ impl Engine {
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
@@ -574,12 +574,13 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||
&mut self,
|
||||
get_fn: impl Fn(&mut T, X) -> V + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@@ -600,7 +601,7 @@ impl Engine {
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
@@ -642,7 +643,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get_result<
|
||||
T: Variant + Clone,
|
||||
@@ -652,6 +653,7 @@ impl Engine {
|
||||
&mut self,
|
||||
get_fn: impl Fn(&mut T, X) -> Result<V, Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@@ -670,7 +672,7 @@ impl 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
|
||||
///
|
||||
@@ -712,12 +714,13 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||
&mut self,
|
||||
set_fn: impl Fn(&mut T, X, V) + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@@ -736,7 +739,7 @@ impl 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
|
||||
///
|
||||
@@ -781,7 +784,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_set_result<
|
||||
T: Variant + Clone,
|
||||
@@ -791,6 +794,7 @@ impl Engine {
|
||||
&mut self,
|
||||
set_fn: impl Fn(&mut T, X, V) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
) -> &mut Self {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<T>() == TypeId::of::<Array>() {
|
||||
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`].
|
||||
///
|
||||
/// Not available under `no_index`.
|
||||
/// Not available under both `no_index` and `no_object`.
|
||||
///
|
||||
/// # Panics
|
||||
///
|
||||
@@ -851,7 +855,7 @@ impl Engine {
|
||||
/// # Ok(())
|
||||
/// # }
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn register_indexer_get_set<T: Variant + Clone, X: Variant + Clone, V: Variant + Clone>(
|
||||
&mut self,
|
||||
|
@@ -375,12 +375,8 @@ impl Engine {
|
||||
crate::engine::FN_IDX_GET => {
|
||||
assert!(args.len() == 2);
|
||||
|
||||
EvalAltResult::ErrorFunctionNotFound(
|
||||
format!(
|
||||
"{} [{}]",
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
EvalAltResult::ErrorIndexingType(
|
||||
self.map_type_name(args[0].type_name()).to_string(),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
@@ -391,12 +387,8 @@ impl Engine {
|
||||
crate::engine::FN_IDX_SET => {
|
||||
assert!(args.len() == 3);
|
||||
|
||||
EvalAltResult::ErrorFunctionNotFound(
|
||||
format!(
|
||||
"{} [{}]=",
|
||||
self.map_type_name(args[0].type_name()),
|
||||
self.map_type_name(args[1].type_name()),
|
||||
),
|
||||
EvalAltResult::ErrorIndexingType(
|
||||
self.map_type_name(args[0].type_name()).to_string(),
|
||||
pos,
|
||||
)
|
||||
.into()
|
||||
|
@@ -24,7 +24,6 @@ use std::{
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
use crate::Array;
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
use crate::Map;
|
||||
|
||||
@@ -956,7 +955,7 @@ impl Module {
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn set_indexer_get_fn<ARGS, A, B, T, F>(&mut self, func: F) -> u64
|
||||
where
|
||||
@@ -966,6 +965,7 @@ impl Module {
|
||||
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
|
||||
F: Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
|
||||
{
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@@ -1016,7 +1016,7 @@ impl Module {
|
||||
/// });
|
||||
/// assert!(module.contains_fn(hash));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn set_indexer_set_fn<ARGS, A, B, C, F>(&mut self, func: F) -> u64
|
||||
where
|
||||
@@ -1026,6 +1026,7 @@ impl Module {
|
||||
F: RegisterNativeFunction<ARGS, Result<(), Box<EvalAltResult>>>,
|
||||
F: Fn(&mut A, B, C) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
|
||||
{
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
if TypeId::of::<A>() == TypeId::of::<Array>() {
|
||||
panic!("Cannot register indexer for arrays.");
|
||||
}
|
||||
@@ -1082,7 +1083,7 @@ impl Module {
|
||||
/// assert!(module.contains_fn(hash_get));
|
||||
/// assert!(module.contains_fn(hash_set));
|
||||
/// ```
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[inline(always)]
|
||||
pub fn set_indexer_get_set_fn<A, B, T>(
|
||||
&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) {
|
||||
// map.string
|
||||
(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.
|
||||
// All other items can be thrown away.
|
||||
state.set_dirty();
|
||||
|
@@ -232,10 +232,7 @@ impl Expr {
|
||||
Self::Property(Box::new((
|
||||
(getter, hash_get),
|
||||
(setter, hash_set),
|
||||
Ident {
|
||||
name: state.get_identifier(ident),
|
||||
pos,
|
||||
},
|
||||
(state.get_identifier(ident).into(), pos),
|
||||
)))
|
||||
}
|
||||
_ => self,
|
||||
@@ -1541,10 +1538,7 @@ fn make_dot_expr(
|
||||
let rhs = Expr::Property(Box::new((
|
||||
(getter, hash_get),
|
||||
(setter, hash_set),
|
||||
Ident {
|
||||
name: state.get_identifier(ident),
|
||||
pos: var_pos,
|
||||
},
|
||||
(state.get_identifier(ident).into(), var_pos),
|
||||
)));
|
||||
|
||||
Expr::Dot(Box::new(BinaryExpr { lhs, rhs }), op_pos)
|
||||
|
Reference in New Issue
Block a user