Fix bug in indexing.
This commit is contained in:
parent
106c8ab5d1
commit
b8485b1909
@ -1,6 +1,15 @@
|
||||
Rhai Release Notes
|
||||
==================
|
||||
|
||||
Version 1.0.1
|
||||
=============
|
||||
|
||||
Bug fixes
|
||||
---------
|
||||
|
||||
* Fixed bug in using indexing/dotting inside index bracket.
|
||||
|
||||
|
||||
Version 1.0.0
|
||||
=============
|
||||
|
||||
|
34
src/ast.rs
34
src/ast.rs
@ -1755,10 +1755,10 @@ pub enum Expr {
|
||||
Stmt(Box<StmtBlock>),
|
||||
/// func `(` expr `,` ... `)`
|
||||
FnCall(Box<FnCallExpr>, Position),
|
||||
/// lhs `.` rhs
|
||||
Dot(Box<BinaryExpr>, Position),
|
||||
/// expr `[` expr `]`
|
||||
Index(Box<BinaryExpr>, Position),
|
||||
/// lhs `.` rhs - bool variable is a dummy
|
||||
Dot(Box<BinaryExpr>, bool, Position),
|
||||
/// expr `[` expr `]` - boolean indicates whether the dotting/indexing chain stops
|
||||
Index(Box<BinaryExpr>, bool, Position),
|
||||
/// lhs `&&` rhs
|
||||
And(Box<BinaryExpr>, Position),
|
||||
/// lhs `||` rhs
|
||||
@ -1835,10 +1835,18 @@ impl fmt::Debug for Expr {
|
||||
}
|
||||
ff.finish()
|
||||
}
|
||||
Self::Dot(x, pos) | Self::Index(x, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
|
||||
Self::Index(x, term, pos) => {
|
||||
display_pos = *pos;
|
||||
|
||||
f.debug_struct("Index")
|
||||
.field("lhs", &x.lhs)
|
||||
.field("rhs", &x.rhs)
|
||||
.field("terminate", term)
|
||||
.finish()
|
||||
}
|
||||
Self::Dot(x, _, pos) | Self::And(x, pos) | Self::Or(x, pos) => {
|
||||
let op_name = match self {
|
||||
Self::Dot(_, _) => "Dot",
|
||||
Self::Index(_, _) => "Index",
|
||||
Self::Dot(_, _, _) => "Dot",
|
||||
Self::And(_, _) => "And",
|
||||
Self::Or(_, _) => "Or",
|
||||
_ => unreachable!(),
|
||||
@ -1969,7 +1977,7 @@ impl Expr {
|
||||
Self::Property(x) => (x.2).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, _, _) => {
|
||||
x.lhs.position()
|
||||
}
|
||||
}
|
||||
@ -1991,8 +1999,8 @@ impl Expr {
|
||||
| Self::Map(_, pos)
|
||||
| Self::And(_, pos)
|
||||
| Self::Or(_, pos)
|
||||
| Self::Dot(_, pos)
|
||||
| Self::Index(_, pos)
|
||||
| Self::Dot(_, _, pos)
|
||||
| Self::Index(_, _, pos)
|
||||
| Self::Variable(_, pos, _)
|
||||
| Self::Stack(_, pos)
|
||||
| Self::FnCall(_, pos)
|
||||
@ -2083,8 +2091,8 @@ impl Expr {
|
||||
| Self::InterpolatedString(_, _)
|
||||
| Self::FnCall(_, _)
|
||||
| Self::Stmt(_)
|
||||
| Self::Dot(_, _)
|
||||
| Self::Index(_, _)
|
||||
| Self::Dot(_, _, _)
|
||||
| Self::Index(_, _, _)
|
||||
| Self::Array(_, _)
|
||||
| Self::Map(_, _) => match token {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@ -2149,7 +2157,7 @@ impl Expr {
|
||||
}
|
||||
}
|
||||
}
|
||||
Self::Index(x, _) | Self::Dot(x, _) | Expr::And(x, _) | Expr::Or(x, _) => {
|
||||
Self::Index(x, _, _) | Self::Dot(x, _, _) | Expr::And(x, _) | Expr::Or(x, _) => {
|
||||
if !x.lhs.walk(path, on_node) {
|
||||
return false;
|
||||
}
|
||||
|
127
src/engine.rs
127
src/engine.rs
@ -293,10 +293,10 @@ pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
|
||||
enum ChainType {
|
||||
/// Indexing.
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Index,
|
||||
Indexing,
|
||||
/// Dotting.
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Dot,
|
||||
Dotting,
|
||||
}
|
||||
|
||||
/// Value of a chaining argument.
|
||||
@ -324,6 +324,7 @@ impl ChainArgument {
|
||||
pub fn as_index_value(self) -> Option<Dynamic> {
|
||||
match self {
|
||||
Self::IndexValue(value, _) => Some(value),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
@ -355,6 +356,18 @@ impl From<(Dynamic, Position)> for ChainArgument {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the chaining type for an [`Expr`].
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
fn match_chaining_type(expr: &Expr) -> ChainType {
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _, _) => ChainType::Indexing,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _, _) => ChainType::Dotting,
|
||||
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
/// A type that encapsulates a mutation target for an expression with side effects.
|
||||
#[derive(Debug)]
|
||||
pub enum Target<'a> {
|
||||
@ -1230,21 +1243,12 @@ impl Engine {
|
||||
target: &mut Target,
|
||||
root: (&str, Position),
|
||||
rhs: &Expr,
|
||||
_terminate_chaining: bool,
|
||||
idx_values: &mut StaticVec<ChainArgument>,
|
||||
chain_type: ChainType,
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||
fn match_chain_type(expr: &Expr) -> ChainType {
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _) => ChainType::Index,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _) => ChainType::Dot,
|
||||
_ => unreachable!("`expr` should only be `Index` or `Dot`, but got {:?}", expr),
|
||||
}
|
||||
}
|
||||
|
||||
let is_ref_mut = target.is_ref();
|
||||
|
||||
// Pop the last index value
|
||||
@ -1254,7 +1258,7 @@ impl Engine {
|
||||
|
||||
match chain_type {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
ChainType::Index => {
|
||||
ChainType::Indexing => {
|
||||
let pos = rhs.position();
|
||||
let idx_val = idx_val
|
||||
.as_index_value()
|
||||
@ -1262,15 +1266,17 @@ impl Engine {
|
||||
|
||||
match rhs {
|
||||
// xxx[idx].expr... | xxx[idx][expr]...
|
||||
Expr::Dot(x, x_pos) | Expr::Index(x, x_pos) => {
|
||||
Expr::Dot(x, term, x_pos) | Expr::Index(x, term, x_pos)
|
||||
if !_terminate_chaining =>
|
||||
{
|
||||
let idx_pos = x.lhs.position();
|
||||
let obj_ptr = &mut self.get_indexed_mut(
|
||||
mods, state, lib, target, idx_val, idx_pos, false, true, level,
|
||||
)?;
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
|
||||
let rhs_chain = match_chaining_type(rhs);
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, idx_values,
|
||||
mods, state, lib, this_ptr, obj_ptr, root, &x.rhs, *term, idx_values,
|
||||
rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))
|
||||
@ -1327,7 +1333,7 @@ impl Engine {
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
ChainType::Dot => {
|
||||
ChainType::Dotting => {
|
||||
match rhs {
|
||||
// xxx.fn_name(arg_expr_list)
|
||||
Expr::FnCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
|
||||
@ -1485,7 +1491,9 @@ impl Engine {
|
||||
)
|
||||
}
|
||||
// {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, term, x_pos) | Expr::Dot(x, term, x_pos)
|
||||
if target.is::<Map>() =>
|
||||
{
|
||||
let val_target = &mut match x.lhs {
|
||||
Expr::Property(ref p) => {
|
||||
let (name, pos) = &p.2;
|
||||
@ -1512,22 +1520,22 @@ impl Engine {
|
||||
// Others - syntax error
|
||||
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||
};
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
let rhs_chain = match_chaining_type(rhs);
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, val_target, root, &x.rhs, idx_values,
|
||||
rhs_chain, level, new_val,
|
||||
mods, state, lib, this_ptr, val_target, root, &x.rhs, *term,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(*x_pos))
|
||||
}
|
||||
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
|
||||
Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) => {
|
||||
Expr::Index(x, term, x_pos) | Expr::Dot(x, term, x_pos) => {
|
||||
match x.lhs {
|
||||
// xxx.prop[expr] | xxx.prop.expr
|
||||
Expr::Property(ref p) => {
|
||||
let ((getter, hash_get), (setter, hash_set), (name, pos)) =
|
||||
p.as_ref();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
let rhs_chain = match_chaining_type(rhs);
|
||||
let hash_get = FnCallHashes::from_native(*hash_get);
|
||||
let hash_set = FnCallHashes::from_native(*hash_set);
|
||||
let mut arg_values = [target.as_mut(), &mut Default::default()];
|
||||
@ -1567,6 +1575,7 @@ impl Engine {
|
||||
&mut val.into(),
|
||||
root,
|
||||
&x.rhs,
|
||||
*term,
|
||||
idx_values,
|
||||
rhs_chain,
|
||||
level,
|
||||
@ -1617,7 +1626,7 @@ impl Engine {
|
||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||
Expr::FnCall(ref f, pos) if !f.is_qualified() => {
|
||||
let FnCallExpr { name, hashes, .. } = f.as_ref();
|
||||
let rhs_chain = match_chain_type(rhs);
|
||||
let rhs_chain = match_chaining_type(rhs);
|
||||
let args = &mut idx_val
|
||||
.as_fn_call_args()
|
||||
.expect("never fails because `chain_type` is `ChainType::Dot` with `Expr::FnCallExpr`");
|
||||
@ -1628,8 +1637,8 @@ impl Engine {
|
||||
let target = &mut val.into();
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, target, root, &x.rhs, idx_values,
|
||||
rhs_chain, level, new_val,
|
||||
mods, state, lib, this_ptr, target, root, &x.rhs, *term,
|
||||
idx_values, rhs_chain, level, new_val,
|
||||
)
|
||||
.map_err(|err| err.fill_position(pos))
|
||||
}
|
||||
@ -1663,18 +1672,18 @@ impl Engine {
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
) -> RhaiResult {
|
||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, term, op_pos) = match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||
Expr::Index(x, term, pos) => (x.as_ref(), ChainType::Indexing, *term, *pos),
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x, pos) => (x.as_ref(), ChainType::Dot, *pos),
|
||||
Expr::Dot(x, term, pos) => (x.as_ref(), ChainType::Dotting, *term, *pos),
|
||||
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
||||
};
|
||||
|
||||
let idx_values = &mut Default::default();
|
||||
|
||||
self.eval_indexed_chain(
|
||||
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, 0, level,
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, mods, state, lib, this_ptr, rhs, term, chain_type, idx_values, 0, level,
|
||||
)?;
|
||||
|
||||
match lhs {
|
||||
@ -1687,9 +1696,10 @@ impl Engine {
|
||||
|
||||
let obj_ptr = &mut target.into();
|
||||
let root = (x.2.as_str(), *var_pos);
|
||||
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, &mut None, obj_ptr, root, rhs, idx_values, chain_type, level,
|
||||
new_val,
|
||||
mods, state, lib, &mut None, obj_ptr, root, rhs, term, idx_values, chain_type,
|
||||
level, new_val,
|
||||
)
|
||||
.map(|(v, _)| v)
|
||||
.map_err(|err| err.fill_position(op_pos))
|
||||
@ -1702,8 +1712,8 @@ impl Engine {
|
||||
let obj_ptr = &mut value.into();
|
||||
let root = ("", expr.position());
|
||||
self.eval_dot_index_chain_helper(
|
||||
mods, state, lib, this_ptr, obj_ptr, root, rhs, idx_values, chain_type, level,
|
||||
new_val,
|
||||
mods, state, lib, this_ptr, obj_ptr, root, rhs, term, idx_values, chain_type,
|
||||
level, new_val,
|
||||
)
|
||||
.map(|(v, _)| v)
|
||||
.map_err(|err| err.fill_position(op_pos))
|
||||
@ -1716,7 +1726,7 @@ impl Engine {
|
||||
/// just a few levels of indexing.
|
||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||
#[must_use]
|
||||
fn eval_indexed_chain(
|
||||
fn eval_dot_index_chain_arguments(
|
||||
&self,
|
||||
scope: &mut Scope,
|
||||
mods: &mut Imports,
|
||||
@ -1724,6 +1734,7 @@ impl Engine {
|
||||
lib: &[&Module],
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
expr: &Expr,
|
||||
terminate_chaining: bool,
|
||||
_parent_chain_type: ChainType,
|
||||
idx_values: &mut StaticVec<ChainArgument>,
|
||||
size: usize,
|
||||
@ -1734,7 +1745,7 @@ impl Engine {
|
||||
|
||||
match expr {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dot && !x.is_qualified() => {
|
||||
Expr::FnCall(x, _) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => {
|
||||
let crate::ast::FnCallExpr {
|
||||
args, constants, ..
|
||||
} = x.as_ref();
|
||||
@ -1754,30 +1765,30 @@ impl Engine {
|
||||
idx_values.push((arg_values, first_arg_pos).into());
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||
Expr::Property(x) if _parent_chain_type == ChainType::Dotting => {
|
||||
idx_values.push(ChainArgument::Property((x.2).1))
|
||||
}
|
||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
Expr::Index(x, _) | Expr::Dot(x, _) => {
|
||||
Expr::Index(x, term, _) | Expr::Dot(x, term, _) if !terminate_chaining => {
|
||||
let crate::ast::BinaryExpr { lhs, rhs, .. } = x.as_ref();
|
||||
|
||||
// Evaluate in left-to-right order
|
||||
let lhs_val = match lhs {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Property(x) if _parent_chain_type == ChainType::Dot => {
|
||||
Expr::Property(x) if _parent_chain_type == ChainType::Dotting => {
|
||||
ChainArgument::Property((x.2).1)
|
||||
}
|
||||
Expr::Property(_) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::FnCall(x, _)
|
||||
if _parent_chain_type == ChainType::Dot && !x.is_qualified() =>
|
||||
if _parent_chain_type == ChainType::Dotting && !x.is_qualified() =>
|
||||
{
|
||||
let crate::ast::FnCallExpr {
|
||||
args, constants, ..
|
||||
@ -1798,41 +1809,37 @@ impl Engine {
|
||||
(arg_values, first_arg_pos).into()
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dot => {
|
||||
Expr::FnCall(_, _) if _parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
expr if _parent_chain_type == ChainType::Dot => {
|
||||
expr if _parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("invalid dot expression: {:?}", expr);
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if _parent_chain_type == ChainType::Index => self
|
||||
_ if _parent_chain_type == ChainType::Indexing => self
|
||||
.eval_expr(scope, mods, state, lib, this_ptr, lhs, level)
|
||||
.map(|v| (v.flatten(), lhs.position()).into())?,
|
||||
expr => unreachable!("unknown chained expression: {:?}", expr),
|
||||
};
|
||||
|
||||
// Push in reverse order
|
||||
let chain_type = match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _) => ChainType::Index,
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _) => ChainType::Dot,
|
||||
_ => unreachable!("index or dot chain expected, but gets {:?}", expr),
|
||||
};
|
||||
self.eval_indexed_chain(
|
||||
scope, mods, state, lib, this_ptr, rhs, chain_type, idx_values, size, level,
|
||||
let chain_type = match_chaining_type(expr);
|
||||
|
||||
self.eval_dot_index_chain_arguments(
|
||||
scope, mods, state, lib, this_ptr, rhs, *term, chain_type, idx_values, size,
|
||||
level,
|
||||
)?;
|
||||
|
||||
idx_values.push(lhs_val);
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
_ if _parent_chain_type == ChainType::Dot => {
|
||||
_ if _parent_chain_type == ChainType::Dotting => {
|
||||
unreachable!("invalid dot expression: {:?}", expr);
|
||||
}
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if _parent_chain_type == ChainType::Index => idx_values.push(
|
||||
_ if _parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||
self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)
|
||||
.map(|v| (v.flatten(), expr.position()).into())?,
|
||||
),
|
||||
@ -2054,13 +2061,13 @@ impl Engine {
|
||||
|
||||
// lhs[idx_expr]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _) => {
|
||||
Expr::Index(_, _, _) => {
|
||||
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
|
||||
}
|
||||
|
||||
// lhs.dot_rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _) => {
|
||||
Expr::Dot(_, _, _) => {
|
||||
self.eval_dot_index_chain(scope, mods, state, lib, this_ptr, expr, level, None)
|
||||
}
|
||||
|
||||
@ -2449,7 +2456,7 @@ impl Engine {
|
||||
}
|
||||
// idx_lhs[idx_expr] op= rhs
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(_, _) => {
|
||||
Expr::Index(_, _, _) => {
|
||||
self.eval_dot_index_chain(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||
)?;
|
||||
@ -2457,7 +2464,7 @@ impl Engine {
|
||||
}
|
||||
// dot_lhs.dot_rhs op= rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(_, _) => {
|
||||
Expr::Dot(_, _, _) => {
|
||||
self.eval_dot_index_chain(
|
||||
scope, mods, state, lib, this_ptr, lhs_expr, level, _new_val,
|
||||
)?;
|
||||
|
@ -706,7 +706,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
}
|
||||
// lhs.rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
||||
Expr::Dot(x,_, _) if !_chaining => 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.0.as_str();
|
||||
@ -724,11 +724,11 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
}
|
||||
// ....lhs.rhs
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Dot(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||
Expr::Dot(x,_, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||
|
||||
// lhs[rhs]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
||||
Expr::Index(x, _, _) if !_chaining => match (&mut x.lhs, &mut x.rhs) {
|
||||
// array[int]
|
||||
(Expr::Array(a, pos), Expr::IntegerConstant(i, _))
|
||||
if *i >= 0 && (*i as usize) < a.len() && a.iter().all(Expr::is_pure) =>
|
||||
@ -792,7 +792,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
||||
},
|
||||
// ...[lhs][rhs]
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
Expr::Index(x, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||
Expr::Index(x, _, _) => { optimize_expr(&mut x.lhs, state, false); optimize_expr(&mut x.rhs, state, _chaining); }
|
||||
// ``
|
||||
Expr::InterpolatedString(x, pos) if x.is_empty() => {
|
||||
state.set_dirty();
|
||||
|
72
src/parse.rs
72
src/parse.rs
@ -700,12 +700,14 @@ fn parse_index_chain(
|
||||
// Indexing binds to right
|
||||
Ok(Expr::Index(
|
||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||
false,
|
||||
prev_pos,
|
||||
))
|
||||
}
|
||||
// Otherwise terminate the indexing chain
|
||||
_ => Ok(Expr::Index(
|
||||
BinaryExpr { lhs, rhs: idx_expr }.into(),
|
||||
true,
|
||||
settings.pos,
|
||||
)),
|
||||
}
|
||||
@ -1396,7 +1398,7 @@ fn parse_primary(
|
||||
|
||||
let rhs = parse_primary(input, state, lib, settings.level_up())?;
|
||||
|
||||
make_dot_expr(state, expr, rhs, tail_pos)?
|
||||
make_dot_expr(state, expr, false, rhs, tail_pos)?
|
||||
}
|
||||
// Unknown postfix operator
|
||||
(expr, token) => unreachable!(
|
||||
@ -1410,7 +1412,7 @@ fn parse_primary(
|
||||
// Cache the hash key for namespace-qualified variables
|
||||
match root_expr {
|
||||
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
||||
Expr::Index(ref mut x, _) | Expr::Dot(ref mut x, _) => match x.lhs {
|
||||
Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs {
|
||||
Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()),
|
||||
_ => None,
|
||||
},
|
||||
@ -1539,13 +1541,13 @@ fn make_assignment_stmt<'a>(
|
||||
#[must_use]
|
||||
fn check_lvalue(expr: &Expr, parent_is_dot: bool) -> Option<Position> {
|
||||
match expr {
|
||||
Expr::Index(x, _) | Expr::Dot(x, _) if parent_is_dot => match x.lhs {
|
||||
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
|
||||
Expr::Index(x, _, _) | Expr::Dot(x, _, _) if parent_is_dot => match x.lhs {
|
||||
Expr::Property(_) => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
|
||||
ref e => Some(e.position()),
|
||||
},
|
||||
Expr::Index(x, _) | Expr::Dot(x, _) => match x.lhs {
|
||||
Expr::Index(x, _, _) | Expr::Dot(x, _, _) => match x.lhs {
|
||||
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
||||
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _))),
|
||||
_ => check_lvalue(&x.rhs, matches!(expr, Expr::Dot(_, _, _))),
|
||||
},
|
||||
Expr::Property(_) if parent_is_dot => None,
|
||||
Expr::Property(_) => unreachable!("unexpected Expr::Property in indexing"),
|
||||
@ -1587,8 +1589,8 @@ fn make_assignment_stmt<'a>(
|
||||
}
|
||||
}
|
||||
// xxx[???]... = rhs, xxx.prop... = rhs
|
||||
Expr::Index(ref x, _) | Expr::Dot(ref x, _) => {
|
||||
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _))) {
|
||||
Expr::Index(ref x, _, _) | Expr::Dot(ref x, _, _) => {
|
||||
match check_lvalue(&x.rhs, matches!(lhs, Expr::Dot(_, _, _))) {
|
||||
None => match x.lhs {
|
||||
// var[???] = rhs, var.??? = rhs
|
||||
Expr::Variable(_, _, _) => {
|
||||
@ -1645,41 +1647,38 @@ fn parse_op_assignment_stmt(
|
||||
fn make_dot_expr(
|
||||
state: &mut ParseState,
|
||||
lhs: Expr,
|
||||
terminate_chaining: bool,
|
||||
rhs: Expr,
|
||||
op_pos: Position,
|
||||
) -> Result<Expr, ParseError> {
|
||||
Ok(match (lhs, rhs) {
|
||||
// idx_lhs[idx_expr].rhs
|
||||
// Attach dot chain to the bottom level of indexing chain
|
||||
(Expr::Index(mut x, pos), rhs) => {
|
||||
x.rhs = make_dot_expr(state, x.rhs, rhs, op_pos)?;
|
||||
Expr::Index(x, pos)
|
||||
// lhs[???]...[???].rhs
|
||||
(Expr::Index(mut x, false, pos), rhs) if !terminate_chaining => {
|
||||
// Attach dot chain to the bottom level of indexing chain
|
||||
x.rhs = make_dot_expr(state, x.rhs, false, rhs, op_pos)?;
|
||||
Expr::Index(x, false, pos)
|
||||
}
|
||||
// lhs[idx_expr].rhs
|
||||
(Expr::Index(mut x, _, pos), rhs) => {
|
||||
x.rhs = make_dot_expr(state, x.rhs, true, rhs, op_pos)?;
|
||||
Expr::Index(x, false, pos)
|
||||
}
|
||||
// lhs.id
|
||||
(lhs, Expr::Variable(_, var_pos, x)) if x.1.is_none() => {
|
||||
let ident = x.2;
|
||||
let getter = state.get_identifier(crate::engine::make_getter(&ident));
|
||||
let hash_get = calc_fn_hash(&getter, 1);
|
||||
let setter = state.get_identifier(crate::engine::make_setter(&ident));
|
||||
let hash_set = calc_fn_hash(&setter, 2);
|
||||
|
||||
let rhs = Expr::Property(Box::new((
|
||||
(getter, hash_get),
|
||||
(setter, hash_set),
|
||||
(state.get_identifier(ident).into(), var_pos),
|
||||
)));
|
||||
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
(lhs, var_expr @ Expr::Variable(_, _, _)) if var_expr.is_variable_access(true) => {
|
||||
let rhs = var_expr.into_property(state);
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||
}
|
||||
// lhs.module::id - syntax error
|
||||
(_, Expr::Variable(_, _, x)) if x.1.is_some() => {
|
||||
(_, Expr::Variable(_, _, x)) => {
|
||||
return Err(PERR::PropertyExpected
|
||||
.into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos))
|
||||
}
|
||||
// lhs.prop
|
||||
(lhs, prop @ Expr::Property(_)) => Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), op_pos),
|
||||
(lhs, prop @ Expr::Property(_)) => {
|
||||
Expr::Dot(BinaryExpr { lhs, rhs: prop }.into(), false, op_pos)
|
||||
}
|
||||
// lhs.dot_lhs.dot_rhs
|
||||
(lhs, Expr::Dot(x, pos)) => match x.lhs {
|
||||
(lhs, Expr::Dot(x, _, pos)) => match x.lhs {
|
||||
Expr::Variable(_, _, _) | Expr::Property(_) => {
|
||||
let rhs = Expr::Dot(
|
||||
BinaryExpr {
|
||||
@ -1687,9 +1686,10 @@ fn make_dot_expr(
|
||||
rhs: x.rhs,
|
||||
}
|
||||
.into(),
|
||||
false,
|
||||
pos,
|
||||
);
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||
}
|
||||
Expr::FnCall(mut func, func_pos) => {
|
||||
// Recalculate hash
|
||||
@ -1704,23 +1704,25 @@ fn make_dot_expr(
|
||||
rhs: x.rhs,
|
||||
}
|
||||
.into(),
|
||||
false,
|
||||
pos,
|
||||
);
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||
}
|
||||
_ => unreachable!("invalid dot expression: {:?}", x.lhs),
|
||||
},
|
||||
// lhs.idx_lhs[idx_rhs]
|
||||
(lhs, Expr::Index(x, pos)) => {
|
||||
(lhs, Expr::Index(x, term, pos)) => {
|
||||
let rhs = Expr::Index(
|
||||
BinaryExpr {
|
||||
lhs: x.lhs.into_property(state),
|
||||
rhs: x.rhs,
|
||||
}
|
||||
.into(),
|
||||
term,
|
||||
pos,
|
||||
);
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||
}
|
||||
// lhs.nnn::func(...)
|
||||
(_, Expr::FnCall(x, _)) if x.is_qualified() => {
|
||||
@ -1756,7 +1758,7 @@ fn make_dot_expr(
|
||||
calc_fn_hash(&func.name, func.args.len() + 1),
|
||||
);
|
||||
let rhs = Expr::FnCall(func, func_pos);
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), op_pos)
|
||||
Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)
|
||||
}
|
||||
// lhs.rhs
|
||||
(_, rhs) => return Err(PERR::PropertyExpected.into_err(rhs.position())),
|
||||
|
@ -53,6 +53,18 @@ fn test_arrays() -> Result<(), Box<EvalAltResult>> {
|
||||
convert_to_vec::<INT>(engine.eval("let y = [1, 2, 3]; y.insert(-999, 4); y")?),
|
||||
[4, 1, 2, 3]
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let y = [1, 2, 3]; let z = [42]; y[z.len]")?,
|
||||
2
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let y = [1, 2, [3, 4, 5, 6]]; let z = [42]; y[2][z.len]")?,
|
||||
4
|
||||
);
|
||||
assert_eq!(
|
||||
engine.eval::<INT>("let y = [1, 2, 3]; let z = [2]; y[z[0]]")?,
|
||||
3
|
||||
);
|
||||
|
||||
assert_eq!(
|
||||
convert_to_vec::<INT>(engine.eval(
|
||||
|
Loading…
Reference in New Issue
Block a user