Short-circuits op-assignment for indexing and dotting.
This commit is contained in:
parent
93d970235e
commit
123e9d6901
123
src/engine.rs
123
src/engine.rs
@ -1088,7 +1088,7 @@ impl Engine {
|
|||||||
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)>,
|
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
||||||
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
|
||||||
if chain_type == ChainType::NonChaining {
|
if chain_type == ChainType::NonChaining {
|
||||||
unreachable!("should not be ChainType::NonChaining");
|
unreachable!("should not be ChainType::NonChaining");
|
||||||
@ -1128,7 +1128,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
.map_err(|err| err.fill_position(*x_pos))
|
.map_err(|err| err.fill_position(*x_pos))
|
||||||
}
|
}
|
||||||
// xxx[rhs] = new_val
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
_ if new_val.is_some() => {
|
||||||
let idx_val = idx_val.as_index_value();
|
let idx_val = idx_val.as_index_value();
|
||||||
let mut idx_val2 = idx_val.clone();
|
let mut idx_val2 = idx_val.clone();
|
||||||
@ -1139,8 +1139,46 @@ impl Engine {
|
|||||||
) {
|
) {
|
||||||
// Indexed value is a reference - update directly
|
// Indexed value is a reference - update directly
|
||||||
Ok(ref mut obj_ptr) => {
|
Ok(ref mut obj_ptr) => {
|
||||||
let (new_val, new_val_pos) = new_val.unwrap();
|
let ((mut new_val, new_val_pos), (op, op_pos)) = new_val.unwrap();
|
||||||
|
|
||||||
|
if op.is_empty() {
|
||||||
obj_ptr.set_value(new_val, new_val_pos)?;
|
obj_ptr.set_value(new_val, new_val_pos)?;
|
||||||
|
} else {
|
||||||
|
let mut lock_guard;
|
||||||
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
|
if cfg!(not(feature = "no_closure")) && obj_ptr.is_shared() {
|
||||||
|
lock_guard =
|
||||||
|
obj_ptr.as_mut().write_lock::<Dynamic>().unwrap();
|
||||||
|
lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
|
} else {
|
||||||
|
lhs_ptr_inner = obj_ptr.as_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = &mut [lhs_ptr_inner, &mut new_val];
|
||||||
|
|
||||||
|
match self.exec_fn_call(
|
||||||
|
mods, state, lib, op, None, args, true, false, false,
|
||||||
|
op_pos, None, None, level,
|
||||||
|
) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
|
||||||
|
{
|
||||||
|
// Expand to `var = var op rhs`
|
||||||
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
|
||||||
|
// Run function
|
||||||
|
let (value, _) = self.exec_fn_call(
|
||||||
|
mods, state, lib, op, None, args, true, false,
|
||||||
|
false, op_pos, None, None, level,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
*args[0] = value.flatten();
|
||||||
|
}
|
||||||
|
err => return err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
Err(err) => match *err {
|
Err(err) => match *err {
|
||||||
@ -1155,11 +1193,13 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
if let Some(mut new_val) = _call_setter {
|
if let Some(mut new_val) = _call_setter {
|
||||||
let val_type_name = target_val.type_name();
|
let val_type_name = target_val.type_name();
|
||||||
let args = &mut [target_val, &mut idx_val2, &mut new_val.0];
|
let ((_, val_pos), _) = new_val;
|
||||||
|
|
||||||
|
let args = &mut [target_val, &mut idx_val2, &mut (new_val.0).0];
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false,
|
mods, state, lib, FN_IDX_SET, None, args, is_ref, true, false,
|
||||||
new_val.1, None, None, level,
|
val_pos, None, None, level,
|
||||||
)
|
)
|
||||||
.map_err(|err| match *err {
|
.map_err(|err| match *err {
|
||||||
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
EvalAltResult::ErrorFunctionNotFound(fn_sig, _)
|
||||||
@ -1213,7 +1253,7 @@ impl Engine {
|
|||||||
Expr::FnCall(_, _) => {
|
Expr::FnCall(_, _) => {
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
unreachable!("function call in dot chain should not be namespace-qualified")
|
||||||
}
|
}
|
||||||
// {xxx:map}.id = ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
Expr::Property(x) if target_val.is::<Map>() && new_val.is_some() => {
|
||||||
let Ident { name, pos } = &x.2;
|
let Ident { name, pos } = &x.2;
|
||||||
let index = name.clone().into();
|
let index = name.clone().into();
|
||||||
@ -1221,10 +1261,46 @@ impl Engine {
|
|||||||
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
mods, state, lib, target_val, index, *pos, true, is_ref, false, level,
|
||||||
)?;
|
)?;
|
||||||
|
|
||||||
let (new_val, new_val_pos) = new_val.unwrap();
|
let ((mut new_val, new_val_pos), (op, op_pos)) = new_val.unwrap();
|
||||||
val.set_value(new_val, new_val_pos)?;
|
|
||||||
|
|
||||||
Ok((Default::default(), true))
|
if op.is_empty() {
|
||||||
|
val.set_value(new_val, new_val_pos)?;
|
||||||
|
} else {
|
||||||
|
let mut lock_guard;
|
||||||
|
let lhs_ptr_inner;
|
||||||
|
|
||||||
|
if cfg!(not(feature = "no_closure")) && val.is_shared() {
|
||||||
|
lock_guard = val.as_mut().write_lock::<Dynamic>().unwrap();
|
||||||
|
lhs_ptr_inner = lock_guard.deref_mut();
|
||||||
|
} else {
|
||||||
|
lhs_ptr_inner = val.as_mut();
|
||||||
|
}
|
||||||
|
|
||||||
|
let args = &mut [lhs_ptr_inner, &mut new_val];
|
||||||
|
|
||||||
|
match self.exec_fn_call(
|
||||||
|
mods, state, lib, op, None, args, true, false, false, op_pos, None,
|
||||||
|
None, level,
|
||||||
|
) {
|
||||||
|
Ok(_) => (),
|
||||||
|
Err(err) if matches!(err.as_ref(), EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with(op)) =>
|
||||||
|
{
|
||||||
|
// Expand to `var = var op rhs`
|
||||||
|
let op = &op[..op.len() - 1]; // extract operator without =
|
||||||
|
|
||||||
|
// Run function
|
||||||
|
let (value, _) = self.exec_fn_call(
|
||||||
|
mods, state, lib, op, None, args, true, false, false,
|
||||||
|
op_pos, None, None, level,
|
||||||
|
)?;
|
||||||
|
|
||||||
|
*args[0] = value.flatten();
|
||||||
|
}
|
||||||
|
err => return err,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x) if target_val.is::<Map>() => {
|
Expr::Property(x) if target_val.is::<Map>() => {
|
||||||
@ -1240,7 +1316,7 @@ impl Engine {
|
|||||||
Expr::Property(x) if new_val.is_some() => {
|
Expr::Property(x) if new_val.is_some() => {
|
||||||
let (_, setter, Ident { pos, .. }) = x.as_ref();
|
let (_, setter, Ident { pos, .. }) = x.as_ref();
|
||||||
let mut new_val = new_val;
|
let mut new_val = new_val;
|
||||||
let mut args = [target_val, &mut new_val.as_mut().unwrap().0];
|
let mut args = [target_val, &mut (new_val.as_mut().unwrap().0).0];
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos,
|
mods, state, lib, setter, None, &mut args, is_ref, true, false, *pos,
|
||||||
None, None, level,
|
None, None, level,
|
||||||
@ -1401,7 +1477,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Option<&mut Dynamic>,
|
this_ptr: &mut Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
level: usize,
|
level: usize,
|
||||||
new_val: Option<(Dynamic, Position)>,
|
new_val: Option<((Dynamic, Position), (&str, Position))>,
|
||||||
) -> Result<Dynamic, Box<EvalAltResult>> {
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
||||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, op_pos) = match expr {
|
||||||
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
Expr::Index(x, pos) => (x.as_ref(), ChainType::Index, *pos),
|
||||||
@ -2035,6 +2111,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
err => return err.map(|(v, _)| v),
|
err => return err.map(|(v, _)| v),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Dynamic::UNIT)
|
Ok(Dynamic::UNIT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2042,28 +2119,8 @@ impl Engine {
|
|||||||
// lhs op= rhs
|
// lhs op= rhs
|
||||||
Stmt::Assignment(x, op_pos) => {
|
Stmt::Assignment(x, op_pos) => {
|
||||||
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
let (lhs_expr, op, rhs_expr) = x.as_ref();
|
||||||
let mut rhs_val =
|
let rhs_val = self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?;
|
||||||
self.eval_expr(scope, mods, state, lib, this_ptr, rhs_expr, level)?;
|
let _new_val = Some(((rhs_val, rhs_expr.position()), (op.as_ref(), *op_pos)));
|
||||||
|
|
||||||
let _new_val = if op.is_empty() {
|
|
||||||
// Normal assignment
|
|
||||||
Some((rhs_val, rhs_expr.position()))
|
|
||||||
} else {
|
|
||||||
// Op-assignment - always map to `lhs = lhs op rhs`
|
|
||||||
let op = &op[..op.len() - 1]; // extract operator without =
|
|
||||||
let args = &mut [
|
|
||||||
&mut self.eval_expr(scope, mods, state, lib, this_ptr, lhs_expr, level)?,
|
|
||||||
&mut rhs_val,
|
|
||||||
];
|
|
||||||
|
|
||||||
Some(
|
|
||||||
self.exec_fn_call(
|
|
||||||
mods, state, lib, op, None, args, false, false, false, *op_pos, None,
|
|
||||||
None, level,
|
|
||||||
)
|
|
||||||
.map(|(v, _)| (v, rhs_expr.position()))?,
|
|
||||||
)
|
|
||||||
};
|
|
||||||
|
|
||||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||||
match lhs_expr {
|
match lhs_expr {
|
||||||
|
@ -287,6 +287,11 @@ impl Engine {
|
|||||||
|
|
||||||
// See if it is built in.
|
// See if it is built in.
|
||||||
if args.len() == 2 {
|
if args.len() == 2 {
|
||||||
|
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
||||||
|
Some(v) => return Ok((v, false)),
|
||||||
|
None => (),
|
||||||
|
}
|
||||||
|
|
||||||
if is_ref {
|
if is_ref {
|
||||||
let (first, second) = args.split_first_mut().unwrap();
|
let (first, second) = args.split_first_mut().unwrap();
|
||||||
|
|
||||||
@ -295,11 +300,6 @@ impl Engine {
|
|||||||
None => (),
|
None => (),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
match run_builtin_binary_op(fn_name, args[0], args[1])? {
|
|
||||||
Some(v) => return Ok((v, false)),
|
|
||||||
None => (),
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return default value (if any)
|
// Return default value (if any)
|
||||||
|
Loading…
Reference in New Issue
Block a user