Introduce ChainType to prepare for more chaining operators.

This commit is contained in:
Stephen Chung 2020-06-23 17:36:07 +08:00
parent 54c5c139f9
commit 3b1719e0bc

View File

@ -77,6 +77,13 @@ pub const FUNC_SETTER: &str = "set$";
pub const FUNC_INDEXER_GET: &str = "$index$get$"; pub const FUNC_INDEXER_GET: &str = "$index$get$";
pub const FUNC_INDEXER_SET: &str = "$index$set$"; pub const FUNC_INDEXER_SET: &str = "$index$set$";
/// A type specifying the method of chaining.
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
enum ChainType {
Index,
Dot,
}
/// 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)]
enum Target<'a> { enum Target<'a> {
@ -1011,33 +1018,41 @@ impl Engine {
target: &mut Target, target: &mut Target,
rhs: &Expr, rhs: &Expr,
idx_values: &mut StaticVec<Dynamic>, idx_values: &mut StaticVec<Dynamic>,
is_index: bool, chain_type: ChainType,
level: usize, level: usize,
mut new_val: Option<Dynamic>, mut new_val: Option<Dynamic>,
) -> Result<(Dynamic, bool), Box<EvalAltResult>> { ) -> Result<(Dynamic, bool), Box<EvalAltResult>> {
let is_ref = target.is_ref(); let is_ref = target.is_ref();
let is_value = target.is_value(); let is_value = target.is_value();
#[inline(always)]
fn get_chain_type(expr: &Expr) -> ChainType {
match expr {
Expr::Index(_) => ChainType::Index,
Expr::Dot(_) => ChainType::Dot,
_ => unreachable!(),
}
}
// Pop the last index value // Pop the last index value
let mut idx_val = idx_values.pop(); let mut idx_val = idx_values.pop();
if is_index { match chain_type {
#[cfg(feature = "no_index")] #[cfg(not(feature = "no_index"))]
unreachable!(); ChainType::Index => {
let pos = rhs.position(); let pos = rhs.position();
match rhs { match rhs {
// xxx[idx].expr... | xxx[idx][expr]... // xxx[idx].expr... | xxx[idx][expr]...
Expr::Dot(x) | Expr::Index(x) => { Expr::Dot(x) | Expr::Index(x) => {
let (idx, expr, pos) = x.as_ref(); let (idx, expr, pos) = x.as_ref();
let is_idx = matches!(rhs, Expr::Index(_)); let next_chain = get_chain_type(rhs);
let idx_pos = idx.position(); let idx_pos = idx.position();
let this_ptr = let this_ptr = &mut self
&mut self.get_indexed_mut(state, lib, target, idx_val, idx_pos, false)?; .get_indexed_mut(state, lib, target, idx_val, idx_pos, false)?;
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, this_ptr, expr, idx_values, is_idx, level, new_val, state, lib, this_ptr, expr, idx_values, next_chain, level, new_val,
) )
.map_err(|err| EvalAltResult::new_position(err, *pos)) .map_err(|err| EvalAltResult::new_position(err, *pos))
} }
@ -1050,9 +1065,12 @@ impl Engine {
// Try to call an index setter // Try to call an index setter
Ok(this_ptr) if this_ptr.is_value() => { Ok(this_ptr) if this_ptr.is_value() => {
let fn_name = FUNC_INDEXER_SET; let fn_name = FUNC_INDEXER_SET;
let args = &mut [target.as_mut(), &mut idx_val2, &mut new_val.unwrap()]; let args =
&mut [target.as_mut(), &mut idx_val2, &mut new_val.unwrap()];
self.exec_fn_call(state, lib, fn_name, true, 0, args, is_ref, None, 0) self.exec_fn_call(
state, lib, fn_name, true, 0, args, is_ref, None, 0,
)
.or_else(|err| match *err { .or_else(|err| match *err {
// If there is no index setter, no need to set it back because the indexer is read-only // If there is no index setter, no need to set it back because the indexer is read-only
EvalAltResult::ErrorFunctionNotFound(s, _) EvalAltResult::ErrorFunctionNotFound(s, _)
@ -1065,16 +1083,19 @@ impl Engine {
} }
// Indexed value is a reference - update directly // Indexed value is a reference - update directly
Ok(ref mut this_ptr) => { Ok(ref mut this_ptr) => {
this_ptr this_ptr.set_value(new_val.unwrap()).map_err(|err| {
.set_value(new_val.unwrap()) EvalAltResult::new_position(err, rhs.position())
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?; })?;
} }
Err(err) => match *err { Err(err) => match *err {
// No index getter - try to call an index setter // No index getter - try to call an index setter
EvalAltResult::ErrorIndexingType(_, _) => { EvalAltResult::ErrorIndexingType(_, _) => {
let fn_name = FUNC_INDEXER_SET; let fn_name = FUNC_INDEXER_SET;
let args = let args = &mut [
&mut [target.as_mut(), &mut idx_val2, &mut new_val.unwrap()]; target.as_mut(),
&mut idx_val2,
&mut new_val.unwrap(),
];
self.exec_fn_call( self.exec_fn_call(
state, lib, fn_name, true, 0, args, is_ref, None, 0, state, lib, fn_name, true, 0, args, is_ref, None, 0,
@ -1091,7 +1112,10 @@ impl Engine {
.get_indexed_mut(state, lib, target, idx_val, pos, false) .get_indexed_mut(state, lib, target, idx_val, pos, false)
.map(|v| (v.clone_into_dynamic(), false)), .map(|v| (v.clone_into_dynamic(), false)),
} }
} else { }
#[cfg(not(feature = "no_object"))]
ChainType::Dot => {
match rhs { match rhs {
// xxx.fn_name(arg_expr_list) // xxx.fn_name(arg_expr_list)
Expr::FnCall(x) if x.1.is_none() => { Expr::FnCall(x) if x.1.is_none() => {
@ -1128,18 +1152,17 @@ impl Engine {
// xxx.module::fn_name(...) - syntax error // xxx.module::fn_name(...) - syntax error
Expr::FnCall(_) => unreachable!(), Expr::FnCall(_) => unreachable!(),
// {xxx:map}.id = ??? // {xxx:map}.id = ???
#[cfg(not(feature = "no_object"))]
Expr::Property(x) if target.is::<Map>() && new_val.is_some() => { Expr::Property(x) if target.is::<Map>() && new_val.is_some() => {
let ((prop, _, _), pos) = x.as_ref(); let ((prop, _, _), pos) = x.as_ref();
let index = prop.clone().into(); let index = prop.clone().into();
let mut val = self.get_indexed_mut(state, lib, target, index, *pos, true)?; let mut val =
self.get_indexed_mut(state, lib, target, index, *pos, true)?;
val.set_value(new_val.unwrap()) val.set_value(new_val.unwrap())
.map_err(|err| EvalAltResult::new_position(err, rhs.position()))?; .map_err(|err| EvalAltResult::new_position(err, rhs.position()))?;
Ok((Default::default(), true)) Ok((Default::default(), true))
} }
// {xxx:map}.id // {xxx:map}.id
#[cfg(not(feature = "no_object"))]
Expr::Property(x) if target.is::<Map>() => { Expr::Property(x) if target.is::<Map>() => {
let ((prop, _, _), pos) = x.as_ref(); let ((prop, _, _), pos) = x.as_ref();
let index = prop.clone().into(); let index = prop.clone().into();
@ -1163,11 +1186,10 @@ impl Engine {
.map(|(v, _)| (v, false)) .map(|(v, _)| (v, false))
.map_err(|err| EvalAltResult::new_position(err, *pos)) .map_err(|err| EvalAltResult::new_position(err, *pos))
} }
#[cfg(not(feature = "no_object"))]
// {xxx:map}.prop[expr] | {xxx:map}.prop.expr // {xxx:map}.prop[expr] | {xxx:map}.prop.expr
Expr::Index(x) | Expr::Dot(x) if target.is::<Map>() => { Expr::Index(x) | Expr::Dot(x) if target.is::<Map>() => {
let (prop, expr, pos) = x.as_ref(); let (prop, expr, pos) = x.as_ref();
let is_idx = matches!(rhs, Expr::Index(_)); let next_chain = get_chain_type(rhs);
let mut val = if let Expr::Property(p) = prop { let mut val = if let Expr::Property(p) = prop {
let ((prop, _, _), _) = p.as_ref(); let ((prop, _, _), _) = p.as_ref();
@ -1178,14 +1200,14 @@ impl Engine {
}; };
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, &mut val, expr, idx_values, is_idx, level, new_val, state, lib, &mut val, expr, idx_values, next_chain, level, new_val,
) )
.map_err(|err| EvalAltResult::new_position(err, *pos)) .map_err(|err| EvalAltResult::new_position(err, *pos))
} }
// xxx.prop[expr] | xxx.prop.expr // xxx.prop[expr] | xxx.prop.expr
Expr::Index(x) | Expr::Dot(x) => { Expr::Index(x) | Expr::Dot(x) => {
let (prop, expr, pos) = x.as_ref(); let (prop, expr, pos) = x.as_ref();
let is_idx = matches!(rhs, Expr::Index(_)); let next_chain = get_chain_type(rhs);
let args = &mut [target.as_mut(), &mut Default::default()]; let args = &mut [target.as_mut(), &mut Default::default()];
let (mut val, updated) = if let Expr::Property(p) = prop { let (mut val, updated) = if let Expr::Property(p) = prop {
@ -1201,7 +1223,7 @@ impl Engine {
let (result, may_be_changed) = self let (result, may_be_changed) = self
.eval_dot_index_chain_helper( .eval_dot_index_chain_helper(
state, lib, target, expr, idx_values, is_idx, level, new_val, state, lib, target, expr, idx_values, next_chain, level, new_val,
) )
.map_err(|err| EvalAltResult::new_position(err, *pos))?; .map_err(|err| EvalAltResult::new_position(err, *pos))?;
@ -1211,7 +1233,9 @@ impl Engine {
let ((_, _, setter), _) = p.as_ref(); let ((_, _, setter), _) = p.as_ref();
// Re-use args because the first &mut parameter will not be consumed // Re-use args because the first &mut parameter will not be consumed
args[1] = val; args[1] = val;
self.exec_fn_call(state, lib, setter, true, 0, args, is_ref, None, 0) self.exec_fn_call(
state, lib, setter, true, 0, args, is_ref, None, 0,
)
.or_else(|err| match *err { .or_else(|err| match *err {
// If there is no setter, no need to feed it back because the property is read-only // If there is no setter, no need to feed it back because the property is read-only
EvalAltResult::ErrorDotExpr(_, _) => Ok(Default::default()), EvalAltResult::ErrorDotExpr(_, _) => Ok(Default::default()),
@ -1229,6 +1253,9 @@ impl Engine {
))), ))),
} }
} }
_ => unreachable!(),
}
} }
/// Evaluate a dot/index chain. /// Evaluate a dot/index chain.
@ -1241,9 +1268,9 @@ impl Engine {
level: usize, level: usize,
new_val: Option<Dynamic>, new_val: Option<Dynamic>,
) -> Result<Dynamic, Box<EvalAltResult>> { ) -> Result<Dynamic, Box<EvalAltResult>> {
let ((dot_lhs, dot_rhs, op_pos), is_index) = match expr { let ((dot_lhs, dot_rhs, op_pos), chain_type) = match expr {
Expr::Index(x) => (x.as_ref(), true), Expr::Index(x) => (x.as_ref(), ChainType::Index),
Expr::Dot(x) => (x.as_ref(), false), Expr::Dot(x) => (x.as_ref(), ChainType::Dot),
_ => unreachable!(), _ => unreachable!(),
}; };
@ -1272,7 +1299,7 @@ impl Engine {
let this_ptr = &mut target.into(); let this_ptr = &mut target.into();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, this_ptr, dot_rhs, idx_values, is_index, level, new_val, state, lib, this_ptr, dot_rhs, idx_values, chain_type, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| EvalAltResult::new_position(err, *op_pos)) .map_err(|err| EvalAltResult::new_position(err, *op_pos))
@ -1288,7 +1315,7 @@ impl Engine {
let val = self.eval_expr(scope, state, lib, expr, level)?; let val = self.eval_expr(scope, state, lib, expr, level)?;
let this_ptr = &mut val.into(); let this_ptr = &mut val.into();
self.eval_dot_index_chain_helper( self.eval_dot_index_chain_helper(
state, lib, this_ptr, dot_rhs, idx_values, is_index, level, new_val, state, lib, this_ptr, dot_rhs, idx_values, chain_type, level, new_val,
) )
.map(|(v, _)| v) .map(|(v, _)| v)
.map_err(|err| EvalAltResult::new_position(err, *op_pos)) .map_err(|err| EvalAltResult::new_position(err, *op_pos))