Refine op-assignment.

This commit is contained in:
Stephen Chung 2021-04-24 11:55:40 +08:00
parent e58b57b6e7
commit 61b559a58f
5 changed files with 64 additions and 21 deletions

View File

@ -1347,13 +1347,22 @@ pub struct OpAssignment {
}
impl OpAssignment {
pub fn new(op: &'static str) -> Self {
let op2 = &op[..op.len() - 1]; // extract operator without =
/// Create a new [`OpAssignment`].
///
/// # Panics
///
/// Panics if the operator name is not an op-assignment operator.
pub fn new(op: Token) -> Self {
let op_raw = op
.map_op_assignment()
.expect("token must be an op-assignment operator")
.keyword_syntax();
let op_assignment = op.keyword_syntax();
Self {
hash_op_assign: calc_fn_hash(empty(), op, 2),
hash_op: calc_fn_hash(empty(), op2, 2),
op,
hash_op_assign: calc_fn_hash(empty(), op_assignment, 2),
hash_op: calc_fn_hash(empty(), op_raw, 2),
op: op_assignment,
}
}
}

View File

@ -11,6 +11,7 @@ use crate::optimize::OptimizationLevel;
use crate::packages::{Package, StandardPackage};
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::syntax::CustomSyntax;
use crate::token::Token;
use crate::utils::get_hasher;
use crate::{
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
@ -221,14 +222,14 @@ pub const FN_ANONYMOUS: &str = "anon$";
/// Standard equality comparison operator.
pub const OP_EQUALS: &str = "==";
/// Standard concatenation operator.
pub const OP_CONCAT: &str = "+=";
/// Standard method function for containment testing.
///
/// The `in` operator is implemented as a call to this method.
pub const OP_CONTAINS: &str = "contains";
/// Standard concatenation operator token.
pub const TOKEN_OP_CONCAT: Token = Token::PlusAssign;
/// Method of chaining.
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
@ -1792,7 +1793,7 @@ impl Engine {
mods,
state,
lib,
Some(OpAssignment::new(OP_CONCAT)),
Some(OpAssignment::new(TOKEN_OP_CONCAT)),
pos,
(&mut result).into(),
item,

View File

@ -387,7 +387,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
if x.1.is_none()
&& x.0.is_variable_access(true)
&& matches!(&x.2, Expr::FnCall(x2, _)
if Token::lookup_from_syntax(&x2.name).and_then(|t| t.make_op_assignment()).is_some()
if Token::lookup_from_syntax(&x2.name).map(|t| t.has_op_assignment()).unwrap_or(false)
&& x2.args_count() == 2 && x2.args.len() >= 1
&& x2.args[0].get_variable_name(true) == x.0.get_variable_name(true)
) =>
@ -397,7 +397,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut State, preserve_result: bool) {
state.set_dirty();
let op = Token::lookup_from_syntax(&x2.name).unwrap();
let op_assignment = op.make_op_assignment().unwrap();
x.1 = Some(OpAssignment::new(op_assignment.keyword_syntax()));
x.1 = Some(OpAssignment::new(op_assignment));
x.2 = if x2.args.len() > 1 {
mem::take(&mut x2.args[1])
} else {

View File

@ -1409,7 +1409,7 @@ fn parse_unary(
/// Make an assignment statement.
fn make_assignment_stmt<'a>(
op: &'static str,
op: Option<Token>,
state: &mut ParseState,
lhs: Expr,
rhs: Expr,
@ -1432,11 +1432,7 @@ fn make_assignment_stmt<'a>(
}
}
let op_info = if op.is_empty() {
None
} else {
Some(OpAssignment::new(op))
};
let op_info = op.map(|v| OpAssignment::new(v));
match &lhs {
// const_expr = rhs
@ -1516,13 +1512,12 @@ fn parse_op_assignment_stmt(
let (token, token_pos) = input.peek().unwrap();
settings.pos = *token_pos;
let op = match token {
Token::Equals => "",
_ if token.map_op_assignment().is_some() => token.keyword_syntax(),
let (op, pos) = match token {
Token::Equals => (None, input.next().unwrap().1),
_ if token.is_op_assignment() => input.next().map(|(op, pos)| (Some(op), pos)).unwrap(),
_ => return Ok(Stmt::Expr(lhs)),
};
let (_, pos) = input.next().unwrap();
let rhs = parse_expr(input, state, lib, settings.level_up())?;
make_assignment_stmt(op, state, lhs, rhs, pos)
}

View File

@ -578,6 +578,25 @@ impl Token {
}
}
/// Is this token an op-assignment operator?
#[inline]
pub fn is_op_assignment(&self) -> bool {
match self {
Self::PlusAssign
| Self::MinusAssign
| Self::MultiplyAssign
| Self::DivideAssign
| Self::LeftShiftAssign
| Self::RightShiftAssign
| Self::ModuloAssign
| Self::PowerOfAssign
| Self::AndAssign
| Self::OrAssign
| Self::XOrAssign => true,
_ => false,
}
}
/// Get the corresponding operator of the token if it is an op-assignment operator.
pub fn map_op_assignment(&self) -> Option<Self> {
Some(match self {
@ -596,6 +615,25 @@ impl Token {
})
}
/// Has this token a corresponding op-assignment operator?
#[inline]
pub fn has_op_assignment(&self) -> bool {
match self {
Self::Plus
| Self::Minus
| Self::Multiply
| Self::Divide
| Self::LeftShift
| Self::RightShift
| Self::Modulo
| Self::PowerOf
| Self::Ampersand
| Self::Pipe
| Self::XOr => true,
_ => false,
}
}
/// Get the corresponding op-assignment operator of the token.
pub fn make_op_assignment(&self) -> Option<Self> {
Some(match self {