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