Streamline op-assignments.
This commit is contained in:
@@ -130,10 +130,10 @@ impl Engine {
|
||||
_parent: &Expr,
|
||||
rhs: &Expr,
|
||||
_parent_options: ASTFlags,
|
||||
idx_values: &mut StaticVec<super::ChainArgument>,
|
||||
idx_values: &mut StaticVec<ChainArgument>,
|
||||
chain_type: ChainType,
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
new_val: Option<(Dynamic, OpAssignment)>,
|
||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||
let is_ref_mut = target.is_ref();
|
||||
|
||||
@@ -200,7 +200,7 @@ impl Engine {
|
||||
#[cfg(feature = "debugging")]
|
||||
self.run_debugger(scope, global, lib, this_ptr, _parent, level)?;
|
||||
|
||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`");
|
||||
let (new_val, op_info) = new_val.expect("`Some`");
|
||||
let mut idx_val2 = idx_val.clone();
|
||||
|
||||
let try_setter = match self.get_indexed_mut(
|
||||
@@ -209,12 +209,10 @@ impl Engine {
|
||||
// Indexed value is not a temp value - update directly
|
||||
Ok(ref mut obj_ptr) => {
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, op_pos, obj_ptr, root, new_val,
|
||||
level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(new_pos))?;
|
||||
global, caches, lib, op_info, obj_ptr, root, new_val, level,
|
||||
)?;
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(obj_ptr, new_pos)?;
|
||||
self.check_data_size(obj_ptr, op_info.pos)?;
|
||||
None
|
||||
}
|
||||
// Indexed value cannot be referenced - use indexer
|
||||
@@ -228,7 +226,7 @@ impl Engine {
|
||||
let idx = &mut idx_val2;
|
||||
|
||||
// Is this an op-assignment?
|
||||
if op_info.is_some() {
|
||||
if op_info.is_op_assignment() {
|
||||
let idx = &mut idx.clone();
|
||||
// Call the index getter to get the current value
|
||||
if let Ok(val) =
|
||||
@@ -237,14 +235,13 @@ impl Engine {
|
||||
let mut res = val.into();
|
||||
// Run the op-assignment
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, op_pos, &mut res, root,
|
||||
new_val, level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(new_pos))?;
|
||||
global, caches, lib, op_info, &mut res, root, new_val,
|
||||
level,
|
||||
)?;
|
||||
// Replace new value
|
||||
new_val = res.take_or_clone();
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(&new_val, new_pos)?;
|
||||
self.check_data_size(&new_val, op_info.pos)?;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,19 +302,17 @@ impl Engine {
|
||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||
|
||||
let index = x.2.clone().into();
|
||||
let ((new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`");
|
||||
let (new_val, op_info) = new_val.expect("`Some`");
|
||||
{
|
||||
let val_target = &mut self.get_indexed_mut(
|
||||
global, caches, lib, target, index, *pos, true, false, level,
|
||||
)?;
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, op_pos, val_target, root, new_val,
|
||||
level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(new_pos))?;
|
||||
global, caches, lib, op_info, val_target, root, new_val, level,
|
||||
)?;
|
||||
}
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(target.source(), new_pos)?;
|
||||
self.check_data_size(target.source(), op_info.pos)?;
|
||||
Ok((Dynamic::UNIT, true))
|
||||
}
|
||||
// {xxx:map}.id
|
||||
@@ -337,9 +332,9 @@ impl Engine {
|
||||
self.run_debugger(scope, global, lib, this_ptr, rhs, level)?;
|
||||
|
||||
let ((getter, hash_get), (setter, hash_set), name) = x.as_ref();
|
||||
let ((mut new_val, new_pos), (op_info, op_pos)) = new_val.expect("`Some`");
|
||||
let (mut new_val, op_info) = new_val.expect("`Some`");
|
||||
|
||||
if op_info.is_some() {
|
||||
if op_info.is_op_assignment() {
|
||||
let hash = crate::ast::FnCallHashes::from_native(*hash_get);
|
||||
let args = &mut [target.as_mut()];
|
||||
let (mut orig_val, ..) = self
|
||||
@@ -369,10 +364,8 @@ impl Engine {
|
||||
let orig_val = &mut (&mut orig_val).into();
|
||||
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, op_info, op_pos, orig_val, root, new_val,
|
||||
level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(new_pos))?;
|
||||
global, caches, lib, op_info, orig_val, root, new_val, level,
|
||||
)?;
|
||||
}
|
||||
|
||||
new_val = orig_val;
|
||||
@@ -620,7 +613,7 @@ impl Engine {
|
||||
this_ptr: &mut Option<&mut Dynamic>,
|
||||
expr: &Expr,
|
||||
level: usize,
|
||||
new_val: Option<((Dynamic, Position), (Option<OpAssignment>, Position))>,
|
||||
new_val: Option<(Dynamic, OpAssignment)>,
|
||||
) -> RhaiResult {
|
||||
let (crate::ast::BinaryExpr { lhs, rhs }, chain_type, options, op_pos) = match expr {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
@@ -690,7 +683,7 @@ impl Engine {
|
||||
expr: &Expr,
|
||||
parent_options: ASTFlags,
|
||||
_parent_chain_type: ChainType,
|
||||
idx_values: &mut StaticVec<super::ChainArgument>,
|
||||
idx_values: &mut StaticVec<ChainArgument>,
|
||||
size: usize,
|
||||
level: usize,
|
||||
) -> RhaiResultOf<()> {
|
||||
@@ -717,7 +710,7 @@ impl Engine {
|
||||
},
|
||||
)?;
|
||||
|
||||
idx_values.push(super::ChainArgument::from_fn_call_args(values, pos));
|
||||
idx_values.push(ChainArgument::from_fn_call_args(values, pos));
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => {
|
||||
@@ -726,7 +719,7 @@ impl Engine {
|
||||
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Property(.., pos) if _parent_chain_type == ChainType::Dotting => {
|
||||
idx_values.push(super::ChainArgument::Property(*pos))
|
||||
idx_values.push(ChainArgument::Property(*pos))
|
||||
}
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
@@ -739,7 +732,7 @@ impl Engine {
|
||||
let lhs_arg_val = match lhs {
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::Property(.., pos) if _parent_chain_type == ChainType::Dotting => {
|
||||
super::ChainArgument::Property(*pos)
|
||||
ChainArgument::Property(*pos)
|
||||
}
|
||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
||||
|
||||
@@ -762,7 +755,7 @@ impl Engine {
|
||||
Ok::<_, crate::RhaiError>((values, pos))
|
||||
},
|
||||
)?;
|
||||
super::ChainArgument::from_fn_call_args(values, pos)
|
||||
ChainArgument::from_fn_call_args(values, pos)
|
||||
}
|
||||
#[cfg(not(feature = "no_object"))]
|
||||
Expr::MethodCall(..) if _parent_chain_type == ChainType::Dotting => {
|
||||
@@ -776,10 +769,7 @@ impl Engine {
|
||||
_ if _parent_chain_type == ChainType::Indexing => self
|
||||
.eval_expr(scope, global, caches, lib, this_ptr, lhs, level)
|
||||
.map(|v| {
|
||||
super::ChainArgument::from_index_value(
|
||||
v.flatten(),
|
||||
lhs.start_position(),
|
||||
)
|
||||
ChainArgument::from_index_value(v.flatten(), lhs.start_position())
|
||||
})?,
|
||||
expr => unreachable!("unknown chained expression: {:?}", expr),
|
||||
};
|
||||
@@ -802,9 +792,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
_ if _parent_chain_type == ChainType::Indexing => idx_values.push(
|
||||
self.eval_expr(scope, global, caches, lib, this_ptr, expr, level)
|
||||
.map(|v| {
|
||||
super::ChainArgument::from_index_value(v.flatten(), expr.start_position())
|
||||
})?,
|
||||
.map(|v| ChainArgument::from_index_value(v.flatten(), expr.start_position()))?,
|
||||
),
|
||||
_ => unreachable!("unknown chained expression: {:?}", expr),
|
||||
}
|
||||
|
@@ -322,9 +322,13 @@ impl Engine {
|
||||
|
||||
// `... ${...} ...`
|
||||
Expr::InterpolatedString(x, _) => {
|
||||
let mut concat: Dynamic = self.const_empty_string().into();
|
||||
let mut concat = self.const_empty_string().into();
|
||||
let target = &mut concat;
|
||||
let mut result = Ok(Dynamic::UNIT);
|
||||
|
||||
let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE);
|
||||
let root = ("", Position::NONE);
|
||||
|
||||
for expr in x.iter() {
|
||||
let item =
|
||||
match self.eval_expr(scope, global, caches, lib, this_ptr, expr, level) {
|
||||
@@ -335,23 +339,17 @@ impl Engine {
|
||||
}
|
||||
};
|
||||
|
||||
if let Err(err) = self.eval_op_assignment(
|
||||
global,
|
||||
caches,
|
||||
lib,
|
||||
Some(OpAssignment::new(OP_CONCAT)),
|
||||
expr.start_position(),
|
||||
&mut (&mut concat).into(),
|
||||
("", Position::NONE),
|
||||
item,
|
||||
level,
|
||||
) {
|
||||
result = Err(err.fill_position(expr.start_position()));
|
||||
op_info.pos = expr.start_position();
|
||||
|
||||
if let Err(err) = self
|
||||
.eval_op_assignment(global, caches, lib, op_info, target, root, item, level)
|
||||
{
|
||||
result = Err(err);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
result.map(|_| concat)
|
||||
result.map(|_| concat.take_or_clone())
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "no_index"))]
|
||||
|
@@ -110,14 +110,12 @@ impl Engine {
|
||||
}
|
||||
|
||||
/// Evaluate an op-assignment statement.
|
||||
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards.
|
||||
pub(crate) fn eval_op_assignment(
|
||||
&self,
|
||||
global: &mut GlobalRuntimeState,
|
||||
caches: &mut Caches,
|
||||
lib: &[&Module],
|
||||
op_info: Option<OpAssignment>,
|
||||
op_pos: Position,
|
||||
op_info: OpAssignment,
|
||||
target: &mut Target,
|
||||
root: (&str, Position),
|
||||
new_val: Dynamic,
|
||||
@@ -130,13 +128,15 @@ impl Engine {
|
||||
|
||||
let mut new_val = new_val;
|
||||
|
||||
if let Some(OpAssignment {
|
||||
hash_op_assign,
|
||||
hash_op,
|
||||
op_assign,
|
||||
op,
|
||||
}) = op_info
|
||||
{
|
||||
if op_info.is_op_assignment() {
|
||||
let OpAssignment {
|
||||
hash_op_assign,
|
||||
hash_op,
|
||||
op_assign,
|
||||
op,
|
||||
pos: op_pos,
|
||||
} = op_info;
|
||||
|
||||
let mut lock_guard;
|
||||
let lhs_ptr_inner;
|
||||
|
||||
@@ -166,9 +166,11 @@ impl Engine {
|
||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||
{
|
||||
// Expand to `var = var op rhs`
|
||||
let (value, ..) = self.call_native_fn(
|
||||
global, caches, lib, op, hash_op, args, true, false, op_pos, level,
|
||||
)?;
|
||||
let (value, ..) = self
|
||||
.call_native_fn(
|
||||
global, caches, lib, op, hash_op, args, true, false, op_pos, level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(op_info.pos))?;
|
||||
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.check_data_size(&value, root.1)?;
|
||||
@@ -182,7 +184,9 @@ impl Engine {
|
||||
*target.as_mut() = new_val;
|
||||
}
|
||||
|
||||
target.propagate_changed_value()
|
||||
target
|
||||
.propagate_changed_value()
|
||||
.map_err(|err| err.fill_position(op_info.pos))
|
||||
}
|
||||
|
||||
/// Evaluate a statement.
|
||||
@@ -228,7 +232,7 @@ impl Engine {
|
||||
// Then assignments.
|
||||
// We shouldn't do this for too many variants because, soon or later, the added comparisons
|
||||
// will cost more than the mis-predicted `match` branch.
|
||||
if let Stmt::Assignment(x, op_pos) = stmt {
|
||||
if let Stmt::Assignment(x, ..) = stmt {
|
||||
#[cfg(not(feature = "unchecked"))]
|
||||
self.inc_operations(&mut global.num_operations, stmt.position())?;
|
||||
|
||||
@@ -261,9 +265,8 @@ impl Engine {
|
||||
let lhs_ptr = &mut lhs_ptr;
|
||||
|
||||
self.eval_op_assignment(
|
||||
global, caches, lib, *op_info, *op_pos, lhs_ptr, root, rhs_val, level,
|
||||
global, caches, lib, *op_info, lhs_ptr, root, rhs_val, level,
|
||||
)
|
||||
.map_err(|err| err.fill_position(rhs.start_position()))
|
||||
.map(|_| Dynamic::UNIT)
|
||||
} else {
|
||||
search_result.map(|_| Dynamic::UNIT)
|
||||
@@ -279,7 +282,7 @@ impl Engine {
|
||||
.map(Dynamic::flatten);
|
||||
|
||||
if let Ok(rhs_val) = rhs_result {
|
||||
let _new_val = Some(((rhs_val, rhs.start_position()), (*op_info, *op_pos)));
|
||||
let _new_val = Some((rhs_val, *op_info));
|
||||
|
||||
// Must be either `var[index] op= val` or `var.prop op= val`
|
||||
match lhs {
|
||||
|
@@ -272,6 +272,8 @@ impl<'a> Target<'a> {
|
||||
}
|
||||
/// Propagate a changed value back to the original source.
|
||||
/// This has no effect for direct references.
|
||||
///
|
||||
/// [`Position`] in [`EvalAltResult`] is [`NONE`][Position::NONE] and should be set afterwards.
|
||||
#[inline]
|
||||
pub fn propagate_changed_value(&mut self) -> RhaiResultOf<()> {
|
||||
match self {
|
||||
|
Reference in New Issue
Block a user