From 61b559a58f092dfa8da69a4799c04bee54b05647 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 24 Apr 2021 11:55:40 +0800 Subject: [PATCH] Refine op-assignment. --- src/ast.rs | 19 ++++++++++++++----- src/engine.rs | 9 +++++---- src/optimize.rs | 4 ++-- src/parser.rs | 15 +++++---------- src/token.rs | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 64 insertions(+), 21 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 47cf4d16..023302a2 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -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, } } } diff --git a/src/engine.rs b/src/engine.rs index 1251c193..29439d77 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -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, diff --git a/src/optimize.rs b/src/optimize.rs index 3e40d019..941aa695 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -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 { diff --git a/src/parser.rs b/src/parser.rs index b587fb6b..b33ae36e 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1409,7 +1409,7 @@ fn parse_unary( /// Make an assignment statement. fn make_assignment_stmt<'a>( - op: &'static str, + op: Option, 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) } diff --git a/src/token.rs b/src/token.rs index 20a4bc98..490bc232 100644 --- a/src/token.rs +++ b/src/token.rs @@ -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 { 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 { Some(match self {