Optimize op-assignment statement.

This commit is contained in:
Stephen Chung
2020-05-25 20:14:31 +08:00
parent fca140ef55
commit 95e67c48bd
11 changed files with 341 additions and 234 deletions

View File

@@ -1354,10 +1354,11 @@ impl Engine {
let def_value = Some(&def_value);
let pos = rhs.position();
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
let hash_fn =
calc_fn_hash(empty(), op, args.len(), args.iter().map(|a| a.type_id()));
let hashes = (hash_fn, 0);
let hashes = (
// Qualifiers (none) + function name + number of arguments + argument `TypeId`'s.
calc_fn_hash(empty(), op, args.len(), args.iter().map(|a| a.type_id())),
0,
);
let (r, _) = self.call_fn_raw(
None, state, lib, op, hashes, args, false, def_value, pos, level,
@@ -1417,57 +1418,87 @@ impl Engine {
// lhs = rhs
Expr::Assignment(x) => {
let op_pos = x.2;
let rhs_val = self.eval_expr(scope, state, lib, &x.1, level)?;
let lhs_expr = &x.0;
let op = x.1.as_ref();
let op_pos = x.3;
let mut rhs_val = self.eval_expr(scope, state, lib, &x.2, level)?;
match &x.0 {
// name = rhs
Expr::Variable(x) => {
let ((name, pos), modules, hash_var, index) = x.as_ref();
let index = if state.always_search { None } else { *index };
let mod_and_hash = modules.as_ref().map(|m| (m.as_ref(), *hash_var));
let (lhs_ptr, typ) = search_scope(scope, name, mod_and_hash, index, *pos)?;
self.inc_operations(state, *pos)?;
// name op= rhs
if let Expr::Variable(x) = &x.0 {
let ((name, pos), modules, hash_var, index) = x.as_ref();
let index = if state.always_search { None } else { *index };
let mod_and_hash = modules.as_ref().map(|m| (m.as_ref(), *hash_var));
let (lhs_ptr, typ) = search_scope(scope, name, mod_and_hash, index, *pos)?;
self.inc_operations(state, *pos)?;
match typ {
ScopeEntryType::Constant => Err(Box::new(
EvalAltResult::ErrorAssignmentToConstant(name.clone(), *pos),
)),
ScopeEntryType::Normal => {
*lhs_ptr = rhs_val;
Ok(Default::default())
match typ {
ScopeEntryType::Constant => Err(Box::new(
EvalAltResult::ErrorAssignmentToConstant(name.clone(), *pos),
)),
ScopeEntryType::Normal if !op.is_empty() => {
// Complex op-assignment
if run_builtin_op_assignment(op, lhs_ptr, &rhs_val)?.is_none() {
// Not built in, do function call
let mut lhs_val = lhs_ptr.clone();
let args = &mut [&mut lhs_val, &mut rhs_val];
let hash = calc_fn_hash(empty(), op, 2, empty());
*lhs_ptr = self
.exec_fn_call(
state, lib, op, true, hash, args, false, None, op_pos,
level,
)
.map(|(v, _)| v)?;
}
// End variable cannot be a module
ScopeEntryType::Module => unreachable!(),
Ok(Default::default())
}
ScopeEntryType::Normal => {
*lhs_ptr = rhs_val;
Ok(Default::default())
}
// End variable cannot be a module
ScopeEntryType::Module => unreachable!(),
}
// idx_lhs[idx_expr] = rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(x) => {
let new_val = Some(rhs_val);
self.eval_dot_index_chain(
} else {
let new_val = Some(if op.is_empty() {
rhs_val
} else {
// Complex op-assignment - always do function call
let args = &mut [
&mut self.eval_expr(scope, state, lib, lhs_expr, level)?,
&mut rhs_val,
];
let hash = calc_fn_hash(empty(), op, 2, empty());
self.exec_fn_call(
state, lib, op, true, hash, args, false, None, op_pos, level,
)
.map(|(v, _)| v)?
});
match &x.0 {
// name op= rhs
Expr::Variable(_) => unreachable!(),
// idx_lhs[idx_expr] op= rhs
#[cfg(not(feature = "no_index"))]
Expr::Index(x) => self.eval_dot_index_chain(
scope, state, lib, &x.0, &x.1, true, x.2, level, new_val,
)
}
// dot_lhs.dot_rhs = rhs
#[cfg(not(feature = "no_object"))]
Expr::Dot(x) => {
let new_val = Some(rhs_val);
self.eval_dot_index_chain(
),
// dot_lhs.dot_rhs op= rhs
#[cfg(not(feature = "no_object"))]
Expr::Dot(x) => self.eval_dot_index_chain(
scope, state, lib, &x.0, &x.1, false, op_pos, level, new_val,
)
}
// Error assignment to constant
expr if expr.is_constant() => {
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
expr.get_constant_str(),
),
// Error assignment to constant
expr if expr.is_constant() => {
Err(Box::new(EvalAltResult::ErrorAssignmentToConstant(
expr.get_constant_str(),
expr.position(),
)))
}
// Syntax error
expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS(
expr.position(),
)))
))),
}
// Syntax error
expr => Err(Box::new(EvalAltResult::ErrorAssignmentToUnknownLHS(
expr.position(),
))),
}
}
@@ -1675,7 +1706,7 @@ impl Engine {
let result = self.eval_expr(scope, state, lib, expr, level)?;
Ok(match expr.as_ref() {
// If it is an assignment, erase the result at the root
// If it is a simple assignment, erase the result at the root
Expr::Assignment(_) => Default::default(),
_ => result,
})
@@ -2079,3 +2110,85 @@ fn run_builtin_binary_op(
Ok(None)
}
/// Build in common operator assignment implementations to avoid the cost of calling a registered function.
fn run_builtin_op_assignment(
op: &str,
x: &mut Dynamic,
y: &Dynamic,
) -> Result<Option<()>, Box<EvalAltResult>> {
use crate::packages::arithmetic::*;
let args_type = x.type_id();
if y.type_id() != args_type {
return Ok(None);
}
if args_type == TypeId::of::<INT>() {
let x = x.downcast_mut::<INT>().unwrap();
let y = y.downcast_ref::<INT>().unwrap().clone();
#[cfg(not(feature = "unchecked"))]
match op {
"+" => return Ok(Some(*x = add(*x, y)?)),
"-" => return Ok(Some(*x = sub(*x, y)?)),
"*" => return Ok(Some(*x = mul(*x, y)?)),
"/" => return Ok(Some(*x = div(*x, y)?)),
"%" => return Ok(Some(*x = modulo(*x, y)?)),
"~" => return Ok(Some(*x = pow_i_i(*x, y)?)),
">>" => return Ok(Some(*x = shr(*x, y)?)),
"<<" => return Ok(Some(*x = shl(*x, y)?)),
_ => (),
}
#[cfg(feature = "unchecked")]
match op {
"+" => return Ok(Some(*x += y)),
"-" => return Ok(Some(*x -= y)),
"*" => return Ok(Some(*x *= y)),
"/" => return Ok(Some(*x /= y)),
"%" => return Ok(Some(*x %= y)),
"~" => return Ok(Some(*x = pow_i_i_u(x, y))),
">>" => return Ok(Some(*x = shr_u(x, y))),
"<<" => return Ok(Some(*x = shl_u(x, y))),
_ => (),
}
match op {
"&" => return Ok(Some(*x &= y)),
"|" => return Ok(Some(*x |= y)),
"^" => return Ok(Some(*x ^= y)),
_ => (),
}
} else if args_type == TypeId::of::<bool>() {
let x = x.downcast_mut::<bool>().unwrap();
let y = y.downcast_ref::<bool>().unwrap().clone();
match op {
"&" => return Ok(Some(*x = *x && y)),
"|" => return Ok(Some(*x = *x || y)),
_ => (),
}
}
#[cfg(not(feature = "no_float"))]
{
if args_type == TypeId::of::<FLOAT>() {
let x = x.downcast_mut::<FLOAT>().unwrap();
let y = y.downcast_ref::<FLOAT>().unwrap().clone();
match op {
"+" => return Ok(Some(*x += y)),
"-" => return Ok(Some(*x -= y)),
"*" => return Ok(Some(*x *= y)),
"/" => return Ok(Some(*x /= y)),
"%" => return Ok(Some(*x %= y)),
"~" => return Ok(Some(*x = pow_f_f(*x, y)?)),
_ => (),
}
}
}
Ok(None)
}

View File

@@ -380,26 +380,26 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
stmt => Expr::Stmt(Box::new((stmt, x.1))),
},
// id = expr
Expr::Assignment(x) => match x.1 {
//id = id2 = expr2
Expr::Assignment(x2) => match (x.0, x2.0) {
// var = var = expr2 -> var = expr2
Expr::Assignment(x) => match x.2 {
//id = id2 op= expr2
Expr::Assignment(x2) if x.1 == "=" => match (x.0, x2.0) {
// var = var op= expr2 -> var op= expr2
(Expr::Variable(a), Expr::Variable(b))
if a.1.is_none() && b.1.is_none() && a.0 == b.0 && a.3 == b.3 =>
{
// Assignment to the same variable - fold
state.set_dirty();
Expr::Assignment(Box::new((Expr::Variable(a), optimize_expr(x2.1, state), x.2)))
Expr::Assignment(Box::new((Expr::Variable(a), x2.1, optimize_expr(x2.2, state), x.3)))
}
// id1 = id2 = expr2
// id1 = id2 op= expr2
(id1, id2) => {
Expr::Assignment(Box::new((
id1, Expr::Assignment(Box::new((id2, optimize_expr(x2.1, state), x2.2))), x.2,
id1, x.1, Expr::Assignment(Box::new((id2, x2.1, optimize_expr(x2.2, state), x2.3))), x.3,
)))
}
},
// id = expr
expr => Expr::Assignment(Box::new((x.0, optimize_expr(expr, state), x.2))),
// id op= expr
expr => Expr::Assignment(Box::new((x.0, x.1, optimize_expr(expr, state), x.3))),
},
// lhs.rhs

View File

@@ -53,6 +53,10 @@ def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, {
x.extend(y);
Ok(())
});
lib.set_fn_2_mut("+=", |x: &mut Array, y: Array| {
x.extend(y);
Ok(())
});
lib.set_fn_2(
"+",
|mut x: Array, y: Array| {

View File

@@ -42,6 +42,15 @@ def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, {
Ok(())
},
);
lib.set_fn_2_mut(
"+=",
|map1: &mut Map, map2: Map| {
map2.into_iter().for_each(|(key, value)| {
map1.insert(key, value);
});
Ok(())
},
);
lib.set_fn_2(
"+",
|mut map1: Map, map2: Map| {

View File

@@ -105,10 +105,24 @@ def_package!(crate:BasicStringPackage:"Basic string utilities, including printin
Ok(s.into())
},
);
lib.set_fn_2_mut("+=", |s: &mut ImmutableString, ch: char| {
shared_make_mut(s).push(ch);
Ok(())
});
lib.set_fn_2_mut("append", |s: &mut ImmutableString, ch: char| {
shared_make_mut(s).push(ch);
Ok(())
});
lib.set_fn_2_mut(
"+=",
|s: &mut ImmutableString, s2: ImmutableString| {
if !s2.is_empty() {
shared_make_mut(s).push_str(s2.as_str());
}
Ok(())
}
);
lib.set_fn_2_mut(
"append",
|s: &mut ImmutableString, s2: ImmutableString| {

View File

@@ -405,8 +405,8 @@ pub enum Expr {
Option<Dynamic>,
)>,
),
/// expr = expr
Assignment(Box<(Expr, Expr, Position)>),
/// expr op= expr
Assignment(Box<(Expr, Cow<'static, str>, Expr, Position)>),
/// lhs.rhs
Dot(Box<(Expr, Expr, Position)>),
/// expr[expr]
@@ -508,12 +508,13 @@ impl Expr {
Self::Stmt(x) => x.1,
Self::Variable(x) => (x.0).1,
Self::FnCall(x) => (x.0).2,
Self::Assignment(x) => x.0.position(),
Self::And(x) | Self::Or(x) | Self::In(x) => x.2,
Self::True(pos) | Self::False(pos) | Self::Unit(pos) => *pos,
Self::Assignment(x) | Self::Dot(x) | Self::Index(x) => x.0.position(),
Self::Dot(x) | Self::Index(x) => x.0.position(),
}
}
@@ -538,7 +539,7 @@ impl Expr {
Self::True(pos) => *pos = new_pos,
Self::False(pos) => *pos = new_pos,
Self::Unit(pos) => *pos = new_pos,
Self::Assignment(x) => x.2 = new_pos,
Self::Assignment(x) => x.3 = new_pos,
Self::Dot(x) => x.2 = new_pos,
Self::Index(x) => x.2 = new_pos,
}
@@ -879,30 +880,25 @@ fn parse_index_chain<'a>(
}
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x) => {
Expr::FloatConstant(_) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
.into_err(lhs.position()))
}
Expr::CharConstant(x) => {
Expr::CharConstant(_)
| Expr::Assignment(_)
| Expr::And(_)
| Expr::Or(_)
| Expr::In(_)
| Expr::True(_)
| Expr::False(_)
| Expr::Unit(_) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
}
Expr::Assignment(x) | Expr::And(x) | Expr::Or(x) | Expr::In(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.2))
}
Expr::True(pos) | Expr::False(pos) | Expr::Unit(pos) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
.into_err(lhs.position()))
}
_ => (),
@@ -920,32 +916,25 @@ fn parse_index_chain<'a>(
}
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x) => {
Expr::FloatConstant(_) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
.into_err(lhs.position()))
}
Expr::CharConstant(x) => {
Expr::CharConstant(_)
| Expr::Assignment(_)
| Expr::And(_)
| Expr::Or(_)
| Expr::In(_)
| Expr::True(_)
| Expr::False(_)
| Expr::Unit(_) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.1))
}
Expr::Assignment(x) | Expr::And(x) | Expr::Or(x) | Expr::In(x) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(x.2))
}
Expr::True(pos) | Expr::False(pos) | Expr::Unit(pos) => {
return Err(PERR::MalformedIndexExpr(
"Only arrays, object maps and strings can be indexed".into(),
)
.into_err(pos))
.into_err(lhs.position()))
}
_ => (),
@@ -953,46 +942,46 @@ fn parse_index_chain<'a>(
// lhs[float]
#[cfg(not(feature = "no_float"))]
Expr::FloatConstant(x) => {
x @ Expr::FloatConstant(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a float".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// lhs[char]
Expr::CharConstant(x) => {
x @ Expr::CharConstant(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a character".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// lhs[??? = ??? ]
Expr::Assignment(x) => {
x @ Expr::Assignment(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(),
"Array access expects integer index, not an assignment".into(),
)
.into_err(x.2))
.into_err(x.position()))
}
// lhs[()]
Expr::Unit(pos) => {
x @ Expr::Unit(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not ()".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
// lhs[??? && ???], lhs[??? || ???], lhs[??? in ???]
Expr::And(x) | Expr::Or(x) | Expr::In(x) => {
x @ Expr::And(_) | x @ Expr::Or(_) | x @ Expr::In(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
)
.into_err(x.2))
.into_err(x.position()))
}
// lhs[true], lhs[false]
Expr::True(pos) | Expr::False(pos) => {
x @ Expr::True(_) | x @ Expr::False(_) => {
return Err(PERR::MalformedIndexExpr(
"Array access expects integer index, not a boolean".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
// All other expressions
_ => (),
@@ -1391,17 +1380,22 @@ fn parse_unary<'a>(
}
fn make_assignment_stmt<'a>(
fn_name: Cow<'static, str>,
state: &mut ParseState,
lhs: Expr,
rhs: Expr,
pos: Position,
) -> Result<Expr, ParseError> {
match &lhs {
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
Expr::Variable(x) if x.3.is_none() => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
Expr::Variable(x) => {
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
ScopeEntryType::Normal => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
// Constant values cannot be assigned to
ScopeEntryType::Constant => {
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
@@ -1410,11 +1404,15 @@ fn make_assignment_stmt<'a>(
}
}
Expr::Index(x) | Expr::Dot(x) => match &x.0 {
Expr::Variable(x) if x.3.is_none() => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
Expr::Variable(x) if x.3.is_none() => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
Expr::Variable(x) => {
let ((name, name_pos), _, _, index) = x.as_ref();
match state.stack[(state.len() - index.unwrap().get())].1 {
ScopeEntryType::Normal => Ok(Expr::Assignment(Box::new((lhs, rhs, pos)))),
ScopeEntryType::Normal => {
Ok(Expr::Assignment(Box::new((lhs, fn_name.into(), rhs, pos))))
}
// Constant values cannot be assigned to
ScopeEntryType::Constant => {
Err(PERR::AssignmentToConstant(name.clone()).into_err(*name_pos))
@@ -1447,11 +1445,7 @@ fn parse_op_assignment_stmt<'a>(
}
let op = match token {
Token::Equals => {
let pos = eat_token(input, Token::Equals);
let rhs = parse_expr(input, state, level + 1, allow_stmt_expr)?;
return make_assignment_stmt(state, lhs, rhs, pos);
}
Token::Equals => "".into(),
Token::PlusAssign => Token::Plus.syntax(),
Token::MinusAssign => Token::Minus.syntax(),
Token::MultiplyAssign => Token::Multiply.syntax(),
@@ -1467,20 +1461,9 @@ fn parse_op_assignment_stmt<'a>(
_ => return Ok(lhs),
};
input.next();
let lhs_copy = lhs.clone();
let (_, pos) = input.next().unwrap();
let rhs = parse_expr(input, state, level + 1, allow_stmt_expr)?;
// lhs op= rhs -> lhs = op(lhs, rhs)
let mut args = StaticVec::new();
args.push(lhs_copy);
args.push(rhs);
let hash = calc_fn_hash(empty(), &op, args.len(), empty());
let rhs_expr = Expr::FnCall(Box::new(((op, true, pos), None, hash, args, None)));
make_assignment_stmt(state, lhs, rhs_expr, pos)
make_assignment_stmt(op, state, lhs, rhs, pos)
}
/// Make a dot expression.
@@ -1553,33 +1536,26 @@ fn make_dot_expr(
/// Make an 'in' expression.
fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseError> {
match (&lhs, &rhs) {
(_, Expr::IntegerConstant(x)) => {
(_, x @ Expr::IntegerConstant(_))
| (_, x @ Expr::And(_))
| (_, x @ Expr::Or(_))
| (_, x @ Expr::In(_))
| (_, x @ Expr::Assignment(_))
| (_, x @ Expr::True(_))
| (_, x @ Expr::False(_))
| (_, x @ Expr::Unit(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(x.1))
}
(_, Expr::And(x)) | (_, Expr::Or(x)) | (_, Expr::In(x)) | (_, Expr::Assignment(x)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(x.2))
}
(_, Expr::True(pos)) | (_, Expr::False(pos)) | (_, Expr::Unit(pos)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
#[cfg(not(feature = "no_float"))]
(_, Expr::FloatConstant(x)) => {
(_, x @ Expr::FloatConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression expects a string, array or object map".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// "xxx" in "xxxx", 'x' in "xxxx" - OK!
@@ -1588,63 +1564,58 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
// 123.456 in "xxxx"
#[cfg(not(feature = "no_float"))]
(Expr::FloatConstant(x), Expr::StringConstant(_)) => {
(x @ Expr::FloatConstant(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a float".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// 123 in "xxxx"
(Expr::IntegerConstant(x), Expr::StringConstant(_)) => {
(x @ Expr::IntegerConstant(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a number".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// (??? && ???) in "xxxx", (??? || ???) in "xxxx", (??? in ???) in "xxxx",
(Expr::And(x), Expr::StringConstant(_))
| (Expr::Or(x), Expr::StringConstant(_))
| (Expr::In(x), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a boolean".into(),
)
.into_err(x.2))
}
// true in "xxxx", false in "xxxx"
(Expr::True(pos), Expr::StringConstant(_))
| (Expr::False(pos), Expr::StringConstant(_)) => {
(x @ Expr::And(_), Expr::StringConstant(_))
| (x @ Expr::Or(_), Expr::StringConstant(_))
| (x @ Expr::In(_), Expr::StringConstant(_))
| (x @ Expr::True(_), Expr::StringConstant(_))
| (x @ Expr::False(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not a boolean".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
// [???, ???, ???] in "xxxx"
(Expr::Array(x), Expr::StringConstant(_)) => {
(x @ Expr::Array(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not an array".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// #{...} in "xxxx"
(Expr::Map(x), Expr::StringConstant(_)) => {
(x @ Expr::Map(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not an object map".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// (??? = ???) in "xxxx"
(Expr::Assignment(x), Expr::StringConstant(_)) => {
(x @ Expr::Assignment(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not ()".into(),
"'in' expression for a string expects a string, not an assignment".into(),
)
.into_err(x.2))
.into_err(x.position()))
}
// () in "xxxx"
(Expr::Unit(pos), Expr::StringConstant(_)) => {
(x @ Expr::Unit(_), Expr::StringConstant(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for a string expects a string, not ()".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
// "xxx" in #{...}, 'x' in #{...} - OK!
@@ -1652,62 +1623,58 @@ fn make_in_expr(lhs: Expr, rhs: Expr, op_pos: Position) -> Result<Expr, ParseErr
// 123.456 in #{...}
#[cfg(not(feature = "no_float"))]
(Expr::FloatConstant(x), Expr::Map(_)) => {
(x @ Expr::FloatConstant(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a float".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// 123 in #{...}
(Expr::IntegerConstant(x), Expr::Map(_)) => {
(x @ Expr::IntegerConstant(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a number".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// (??? && ???) in #{...}, (??? || ???) in #{...}, (??? in ???) in #{...},
(Expr::And(x), Expr::Map(_))
| (Expr::Or(x), Expr::Map(_))
| (Expr::In(x), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a boolean".into(),
)
.into_err(x.2))
}
// true in #{...}, false in #{...}
(Expr::True(pos), Expr::Map(_)) | (Expr::False(pos), Expr::Map(_)) => {
(x @ Expr::And(_), Expr::Map(_))
| (x @ Expr::Or(_), Expr::Map(_))
| (x @ Expr::In(_), Expr::Map(_))
| (x @ Expr::True(_), Expr::Map(_))
| (x @ Expr::False(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not a boolean".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
// [???, ???, ???] in #{..}
(Expr::Array(x), Expr::Map(_)) => {
(x @ Expr::Array(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not an array".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// #{...} in #{..}
(Expr::Map(x), Expr::Map(_)) => {
(x @ Expr::Map(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not an object map".into(),
)
.into_err(x.1))
.into_err(x.position()))
}
// (??? = ???) in #{...}
(Expr::Assignment(x), Expr::Map(_)) => {
(x @ Expr::Assignment(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not ()".into(),
"'in' expression for an object map expects a string, not an assignment".into(),
)
.into_err(x.2))
.into_err(x.position()))
}
// () in #{...}
(Expr::Unit(pos), Expr::Map(_)) => {
(x @ Expr::Unit(_), Expr::Map(_)) => {
return Err(PERR::MalformedInExpr(
"'in' expression for an object map expects a string, not ()".into(),
)
.into_err(*pos))
.into_err(x.position()))
}
_ => (),