diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 389ac137..e29745a7 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -193,7 +193,7 @@ pub struct FnCallExpr { /// Does this function call capture the parent scope? pub capture_parent_scope: bool, /// Is this function call a native operator? - pub is_native_operator: bool, + pub operator_token: Option, /// [Position] of the function name. pub pos: Position, } @@ -208,8 +208,8 @@ impl fmt::Debug for FnCallExpr { if self.capture_parent_scope { ff.field("capture_parent_scope", &self.capture_parent_scope); } - if self.is_native_operator { - ff.field("is_native_operator", &self.is_native_operator); + if let Some(ref token) = self.operator_token { + ff.field("operator_token", token); } ff.field("hash", &self.hashes) .field("name", &self.name) @@ -673,7 +673,7 @@ impl Expr { hashes: calc_fn_hash(None, f.fn_name(), 1).into(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, - is_native_operator: false, + operator_token: None, pos, } .into(), diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 3b830100..3bbb0177 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -19,16 +19,16 @@ use std::{ /// Exported under the `internals` feature only. /// /// This type may hold a straight assignment (i.e. not an op-assignment). -#[derive(Clone, Copy, Eq, PartialEq, Hash)] +#[derive(Clone, PartialEq, Hash)] pub struct OpAssignment { /// Hash of the op-assignment call. pub hash_op_assign: u64, /// Hash of the underlying operator call (for fallback). pub hash_op: u64, /// Op-assignment operator. - pub op_assign: &'static str, + pub op_assign: Token, /// Underlying operator. - pub op: &'static str, + pub op: Token, /// [Position] of the op-assignment operator. pub pos: Position, } @@ -41,8 +41,8 @@ impl OpAssignment { Self { hash_op_assign: 0, hash_op: 0, - op_assign: "=", - op: "=", + op_assign: Token::Equals, + op: Token::Equals, pos, } } @@ -71,12 +71,11 @@ impl OpAssignment { pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self { let op_raw = op .get_base_op_from_assignment() - .expect("op-assignment operator") - .literal_syntax(); + .expect("op-assignment operator"); Self { hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2), - hash_op: calc_fn_hash(None, op_raw, 2), - op_assign: op.literal_syntax(), + hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2), + op_assign: op.clone(), op: op_raw, pos, } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 0e6869f9..abdd0573 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -50,7 +50,7 @@ impl Engine { idx_values: &mut FnArgsVec, chain_type: ChainType, level: usize, - new_val: Option<(Dynamic, OpAssignment)>, + new_val: Option<(Dynamic, &OpAssignment)>, ) -> RhaiResultOf<(Dynamic, bool)> { let is_ref_mut = target.is_ref(); @@ -558,7 +558,7 @@ impl Engine { this_ptr: &mut Option<&mut Dynamic>, expr: &Expr, level: usize, - new_val: Option<(Dynamic, OpAssignment)>, + new_val: Option<(Dynamic, &OpAssignment)>, ) -> RhaiResult { let chain_type = ChainType::from(expr); let (crate::ast::BinaryExpr { lhs, rhs }, options, op_pos) = match expr { diff --git a/src/eval/expr.rs b/src/eval/expr.rs index ff3abaff..cdbeb95e 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -219,11 +219,15 @@ impl Engine { level: usize, ) -> RhaiResult { let FnCallExpr { - name, hashes, args, .. + name, + hashes, + args, + operator_token, + .. } = expr; // Short-circuit native binary operator call if under Fast Operators mode - if expr.is_native_operator && self.fast_operators() && args.len() == 2 { + if operator_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self .get_arg_value(scope, global, caches, lib, this_ptr, &args[0], level)? .0 @@ -236,7 +240,9 @@ impl Engine { let operands = &mut [&mut lhs, &mut rhs]; - if let Some(func) = get_builtin_binary_op_fn(name, operands[0], operands[1]) { + if let Some(func) = + get_builtin_binary_op_fn(operator_token.as_ref().unwrap(), operands[0], operands[1]) + { // Built-in found let context = (self, name, None, &*global, lib, pos, level + 1).into(); let result = func(context, operands); @@ -278,7 +284,7 @@ impl Engine { args, *hashes, expr.capture_parent_scope, - expr.is_native_operator, + expr.operator_token.as_ref(), pos, level, ) @@ -384,9 +390,9 @@ impl Engine { op_info.pos = expr.start_position(); - if let Err(err) = self - .eval_op_assignment(global, caches, lib, op_info, target, root, item, level) - { + if let Err(err) = self.eval_op_assignment( + global, caches, lib, &op_info, target, root, item, level, + ) { result = Err(err); break; } diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index bfb79908..58f7ce47 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -117,7 +117,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, lib: &[&Module], - op_info: OpAssignment, + op_info: &OpAssignment, target: &mut Target, root: (&str, Position), new_val: Dynamic, @@ -141,14 +141,15 @@ impl Engine { let mut lock_guard = target.write_lock::().unwrap(); - let hash = hash_op_assign; + let hash = *hash_op_assign; let args = &mut [&mut *lock_guard, &mut new_val]; let level = level + 1; if self.fast_operators() { if let Some(func) = get_builtin_op_assignment_fn(op_assign, args[0], args[1]) { // Built-in found - let context = (self, op_assign, None, &*global, lib, op_pos, level).into(); + let op = op_assign.literal_syntax(); + let context = (self, op, None, &*global, lib, *op_pos, level).into(); let result = func(context, args).map(|_| ()); #[cfg(not(feature = "unchecked"))] @@ -158,8 +159,11 @@ impl Engine { } } + let op_assign = op_assign.literal_syntax(); + let op = op.literal_syntax(); + match self.call_native_fn( - global, caches, lib, op_assign, hash, args, true, true, op_pos, level, + global, caches, lib, op_assign, hash, args, true, true, *op_pos, level, ) { Ok(_) => { #[cfg(not(feature = "unchecked"))] @@ -170,7 +174,7 @@ impl Engine { // Expand to `var = var op rhs` *args[0] = self .call_native_fn( - global, caches, lib, op, hash_op, args, true, false, op_pos, level, + global, caches, lib, op, *hash_op, args, true, false, *op_pos, level, ) .map_err(|err| err.fill_position(op_info.pos))? .0 @@ -279,7 +283,7 @@ impl Engine { let lhs_ptr = &mut lhs_ptr; self.eval_op_assignment( - global, caches, lib, *op_info, lhs_ptr, root, rhs_val, level, + global, caches, lib, op_info, lhs_ptr, root, rhs_val, level, ) .map(|_| Dynamic::UNIT) } else { @@ -303,7 +307,7 @@ impl Engine { rhs_val }; - let _new_val = Some((rhs_val, *op_info)); + let _new_val = Some((rhs_val, op_info)); // Must be either `var[index] op= val` or `var.prop op= val` match lhs { diff --git a/src/func/builtin.rs b/src/func/builtin.rs index 4ad42130..9052a5fe 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -2,7 +2,7 @@ use super::call::FnCallArgs; use super::native::FnBuiltin; -use crate::engine::OP_CONTAINS; +use crate::tokenizer::Token; use crate::{Dynamic, ExclusiveRange, ImmutableString, InclusiveRange, INT}; use std::any::TypeId; #[cfg(feature = "no_std")] @@ -67,7 +67,7 @@ fn is_numeric(type_id: TypeId) -> bool { /// /// The return function will be registered as a _method_, so the first parameter cannot be consumed. #[must_use] -pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option { +pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option { let type1 = x.type_id(); let type2 = y.type_id(); @@ -131,46 +131,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(INT => add(as_int, as_int))), - "-" => return Some(impl_op!(INT => subtract(as_int, as_int))), - "*" => return Some(impl_op!(INT => multiply(as_int, as_int))), - "/" => return Some(impl_op!(INT => divide(as_int, as_int))), - "%" => return Some(impl_op!(INT => modulo(as_int, as_int))), - "**" => return Some(impl_op!(INT => power(as_int, as_int))), - ">>" => return Some(impl_op!(INT => shift_right(as_int, as_int))), - "<<" => return Some(impl_op!(INT => shift_left(as_int, as_int))), + Token::Plus => return Some(impl_op!(INT => add(as_int, as_int))), + Token::Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), + Token::Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), + Token::Divide => return Some(impl_op!(INT => divide(as_int, as_int))), + Token::Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), + Token::PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), + Token::RightShift => return Some(impl_op!(INT => shift_right(as_int, as_int))), + Token::LeftShift => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - "+" => return Some(impl_op!(INT => as_int + as_int)), - "-" => return Some(impl_op!(INT => as_int - as_int)), - "*" => return Some(impl_op!(INT => as_int * as_int)), - "/" => return Some(impl_op!(INT => as_int / as_int)), - "%" => return Some(impl_op!(INT => as_int % as_int)), - "**" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - ">>" => return Some(impl_op!(INT => as_int >> as_int)), - "<<" => return Some(impl_op!(INT => as_int << as_int)), + Token::Plus => return Some(impl_op!(INT => as_int + as_int)), + Token::Minus => return Some(impl_op!(INT => as_int - as_int)), + Token::Multiply => return Some(impl_op!(INT => as_int * as_int)), + Token::Divide => return Some(impl_op!(INT => as_int / as_int)), + Token::Modulo => return Some(impl_op!(INT => as_int % as_int)), + Token::PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + Token::RightShift => return Some(impl_op!(INT => as_int >> as_int)), + Token::LeftShift => return Some(impl_op!(INT => as_int << as_int)), _ => (), } return match op { - "==" => Some(impl_op!(INT => as_int == as_int)), - "!=" => Some(impl_op!(INT => as_int != as_int)), - ">" => Some(impl_op!(INT => as_int > as_int)), - ">=" => Some(impl_op!(INT => as_int >= as_int)), - "<" => Some(impl_op!(INT => as_int < as_int)), - "<=" => Some(impl_op!(INT => as_int <= as_int)), - "&" => Some(impl_op!(INT => as_int & as_int)), - "|" => Some(impl_op!(INT => as_int | as_int)), - "^" => Some(impl_op!(INT => as_int ^ as_int)), - ".." => Some(|_, args| { + Token::EqualsTo => Some(impl_op!(INT => as_int == as_int)), + Token::NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), + Token::GreaterThan => Some(impl_op!(INT => as_int > as_int)), + Token::GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), + Token::LessThan => Some(impl_op!(INT => as_int < as_int)), + Token::LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), + Token::Ampersand => Some(impl_op!(INT => as_int & as_int)), + Token::Pipe => Some(impl_op!(INT => as_int | as_int)), + Token::XOr => Some(impl_op!(INT => as_int ^ as_int)), + Token::ExclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..y).into()) }), - "..=" => Some(|_, args| { + Token::InclusiveRange => Some(|_, args| { let x = args[0].as_int().expect(BUILTIN); let y = args[1].as_int().expect(BUILTIN); Ok((x..=y).into()) @@ -181,47 +181,46 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option() { return match op { - "==" => Some(impl_op!(bool => as_bool == as_bool)), - "!=" => Some(impl_op!(bool => as_bool != as_bool)), - ">" => Some(impl_op!(bool => as_bool > as_bool)), - ">=" => Some(impl_op!(bool => as_bool >= as_bool)), - "<" => Some(impl_op!(bool => as_bool < as_bool)), - "<=" => Some(impl_op!(bool => as_bool <= as_bool)), - "&" => Some(impl_op!(bool => as_bool & as_bool)), - "|" => Some(impl_op!(bool => as_bool | as_bool)), - "^" => Some(impl_op!(bool => as_bool ^ as_bool)), + Token::EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), + Token::NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), + Token::GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), + Token::GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), + Token::LessThan => Some(impl_op!(bool => as_bool < as_bool)), + Token::LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), + Token::Ampersand => Some(impl_op!(bool => as_bool & as_bool)), + Token::Pipe => Some(impl_op!(bool => as_bool | as_bool)), + Token::XOr => Some(impl_op!(bool => as_bool ^ as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "+" => Some(impl_op!(ImmutableString + ImmutableString)), - "-" => Some(impl_op!(ImmutableString - ImmutableString)), - "==" => Some(impl_op!(ImmutableString == ImmutableString)), - "!=" => Some(impl_op!(ImmutableString != ImmutableString)), - ">" => Some(impl_op!(ImmutableString > ImmutableString)), - ">=" => Some(impl_op!(ImmutableString >= ImmutableString)), - "<" => Some(impl_op!(ImmutableString < ImmutableString)), - "<=" => Some(impl_op!(ImmutableString <= ImmutableString)), - OP_CONTAINS => Some(impl_op!(ImmutableString.contains(ImmutableString.as_str()))), + Token::Plus => Some(impl_op!(ImmutableString + ImmutableString)), + Token::Minus => Some(impl_op!(ImmutableString - ImmutableString)), + Token::EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), + Token::NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), + Token::GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), + Token::GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), + Token::LessThan => Some(impl_op!(ImmutableString < ImmutableString)), + Token::LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "+" => Some(|_, args| { + Token::Plus => Some(|_, args| { let x = args[0].as_char().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok(format!("{x}{y}").into()) }), - "==" => Some(impl_op!(char => as_char == as_char)), - "!=" => Some(impl_op!(char => as_char != as_char)), - ">" => Some(impl_op!(char => as_char > as_char)), - ">=" => Some(impl_op!(char => as_char >= as_char)), - "<" => Some(impl_op!(char => as_char < as_char)), - "<=" => Some(impl_op!(char => as_char <= as_char)), + Token::EqualsTo => Some(impl_op!(char => as_char == as_char)), + Token::NotEqualsTo => Some(impl_op!(char => as_char != as_char)), + Token::GreaterThan => Some(impl_op!(char => as_char > as_char)), + Token::GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), + Token::LessThan => Some(impl_op!(char => as_char < as_char)), + Token::LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), _ => None, }; } @@ -231,7 +230,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(|_, args| { + Token::Plus => Some(|_, args| { let blob1 = &*args[0].read_lock::().expect(BUILTIN); let blob2 = &*args[1].read_lock::().expect(BUILTIN); @@ -245,16 +244,20 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(Blob == Blob)), - "!=" => Some(impl_op!(Blob != Blob)), + Token::EqualsTo => Some(impl_op!(Blob == Blob)), + Token::NotEqualsTo => Some(impl_op!(Blob != Blob)), _ => None, }; } if type1 == TypeId::of::<()>() { return match op { - "==" => Some(|_, _| Ok(Dynamic::TRUE)), - "!=" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + Token::EqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Token::NotEqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, }; } @@ -265,18 +268,18 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - "+" => Some(impl_op!(FLOAT => $xx + $yy)), - "-" => Some(impl_op!(FLOAT => $xx - $yy)), - "*" => Some(impl_op!(FLOAT => $xx * $yy)), - "/" => Some(impl_op!(FLOAT => $xx / $yy)), - "%" => Some(impl_op!(FLOAT => $xx % $yy)), - "**" => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), - "==" => Some(impl_op!(FLOAT => $xx == $yy)), - "!=" => Some(impl_op!(FLOAT => $xx != $yy)), - ">" => Some(impl_op!(FLOAT => $xx > $yy)), - ">=" => Some(impl_op!(FLOAT => $xx >= $yy)), - "<" => Some(impl_op!(FLOAT => $xx < $yy)), - "<=" => Some(impl_op!(FLOAT => $xx <= $yy)), + Token::Plus => Some(impl_op!(FLOAT => $xx + $yy)), + Token::Minus => Some(impl_op!(FLOAT => $xx - $yy)), + Token::Multiply => Some(impl_op!(FLOAT => $xx * $yy)), + Token::Divide => Some(impl_op!(FLOAT => $xx / $yy)), + Token::Modulo => Some(impl_op!(FLOAT => $xx % $yy)), + Token::PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), + Token::EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), + Token::NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), + Token::GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), + Token::GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), + Token::LessThan => Some(impl_op!(FLOAT => $xx < $yy)), + Token::LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), _ => None, }; } @@ -299,12 +302,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(from Decimal => add($xx, $yy))), - "-" => return Some(impl_op!(from Decimal => subtract($xx, $yy))), - "*" => return Some(impl_op!(from Decimal => multiply($xx, $yy))), - "/" => return Some(impl_op!(from Decimal => divide($xx, $yy))), - "%" => return Some(impl_op!(from Decimal => modulo($xx, $yy))), - "**" => return Some(impl_op!(from Decimal => power($xx, $yy))), + Token::Plus => return Some(impl_op!(from Decimal => add($xx, $yy))), + Token::Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), + Token::Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), + Token::Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), + Token::Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), + Token::PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), _ => () } @@ -313,22 +316,22 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(from Decimal => $xx + $yy)), - "-" => return Some(impl_op!(from Decimal => $xx - $yy)), - "*" => return Some(impl_op!(from Decimal => $xx * $yy)), - "/" => return Some(impl_op!(from Decimal => $xx / $yy)), - "%" => return Some(impl_op!(from Decimal => $xx % $yy)), - "**" => return Some(impl_op!(from Decimal => $xx.powd($yy))), + Token::Plus => return Some(impl_op!(from Decimal => $xx + $yy)), + Token::Minus => return Some(impl_op!(from Decimal => $xx - $yy)), + Token::Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), + Token::Divide => return Some(impl_op!(from Decimal => $xx / $yy)), + Token::Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), + Token::PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), _ => () } return match op { - "==" => Some(impl_op!(from Decimal => $xx == $yy)), - "!=" => Some(impl_op!(from Decimal => $xx != $yy)), - ">" => Some(impl_op!(from Decimal => $xx > $yy)), - ">=" => Some(impl_op!(from Decimal => $xx >= $yy)), - "<" => Some(impl_op!(from Decimal => $xx < $yy)), - "<=" => Some(impl_op!(from Decimal => $xx <= $yy)), + Token::EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), + Token::NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), + Token::GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), + Token::GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), + Token::LessThan => Some(impl_op!(from Decimal => $xx < $yy)), + Token::LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), _ => None }; } @@ -354,17 +357,17 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(|_, args| { + Token::Plus => Some(|_, args| { let x = args[0].as_char().expect(BUILTIN); let y = &*args[1].read_lock::().expect(BUILTIN); Ok(format!("{x}{y}").into()) }), - "==" => Some(impl_op!(get_s1s2(==))), - "!=" => Some(impl_op!(get_s1s2(!=))), - ">" => Some(impl_op!(get_s1s2(>))), - ">=" => Some(impl_op!(get_s1s2(>=))), - "<" => Some(impl_op!(get_s1s2(<))), - "<=" => Some(impl_op!(get_s1s2(<=))), + Token::EqualsTo => Some(impl_op!(get_s1s2(==))), + Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + Token::GreaterThan => Some(impl_op!(get_s1s2(>))), + Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + Token::LessThan => Some(impl_op!(get_s1s2(<))), + Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } @@ -380,45 +383,48 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Some(|_, args| { + Token::Plus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok((x + y).into()) }), - "-" => Some(|_, args| { + Token::Minus => Some(|_, args| { let x = &*args[0].read_lock::().expect(BUILTIN); let y = args[1].as_char().expect(BUILTIN); Ok((x - y).into()) }), - "==" => Some(impl_op!(get_s1s2(==))), - "!=" => Some(impl_op!(get_s1s2(!=))), - ">" => Some(impl_op!(get_s1s2(>))), - ">=" => Some(impl_op!(get_s1s2(>=))), - "<" => Some(impl_op!(get_s1s2(<))), - "<=" => Some(impl_op!(get_s1s2(<=))), - OP_CONTAINS => Some(|_, args| { - let s = &*args[0].read_lock::().expect(BUILTIN); - let c = args[1].as_char().expect(BUILTIN); - Ok(s.contains(c).into()) - }), + Token::EqualsTo => Some(impl_op!(get_s1s2(==))), + Token::NotEqualsTo => Some(impl_op!(get_s1s2(!=))), + Token::GreaterThan => Some(impl_op!(get_s1s2(>))), + Token::GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), + Token::LessThan => Some(impl_op!(get_s1s2(<))), + Token::LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), _ => None, }; } // () op string if (type1, type2) == (TypeId::of::<()>(), TypeId::of::()) { return match op { - "+" => Some(|_, args| Ok(args[1].clone())), - "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), - "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + Token::Plus => Some(|_, args| Ok(args[1].clone())), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), _ => None, }; } // string op () if (type1, type2) == (TypeId::of::(), TypeId::of::<()>()) { return match op { - "+" => Some(|_, args| Ok(args[0].clone())), - "==" | ">" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), - "!=" => Some(|_, _| Ok(Dynamic::TRUE)), + Token::Plus => Some(|_, args| Ok(args[0].clone())), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), _ => None, }; } @@ -428,19 +434,9 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option() { use crate::Blob; - if type2 == TypeId::of::() { - return match op { - OP_CONTAINS => Some(|_, args| { - let blob = &*args[0].read_lock::().expect(BUILTIN); - let x = (args[1].as_int().expect("`INT`") & 0x0000_00ff) as u8; - Ok((!blob.is_empty() && blob.contains(&x)).into()) - }), - _ => None, - }; - } if type2 == TypeId::of::() { return match op { - "+" => Some(|_, args| { + Token::Plus => Some(|_, args| { let mut buf = [0_u8; 4]; let mut blob = args[0].read_lock::().expect(BUILTIN).clone(); let x = args[1].as_char().expect("`char`").encode_utf8(&mut buf); @@ -452,17 +448,6 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option(), TypeId::of::()) { - use crate::Map; - - return match op { - OP_CONTAINS => Some(impl_op!(Map.contains_key(ImmutableString.as_str()))), - _ => None, - }; - } - // Non-compatible ranges if (type1, type2) == ( @@ -476,48 +461,28 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option None, - }; - } if type1 == type2 { return match op { - "==" => Some(impl_op!(ExclusiveRange == ExclusiveRange)), - "!=" => Some(impl_op!(ExclusiveRange != ExclusiveRange)), + Token::EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), + Token::NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), _ => None, }; } } if type1 == TypeId::of::() { - if type2 == TypeId::of::() { - return match op { - OP_CONTAINS => Some(|_, args| { - let range = &*args[0].read_lock::().expect(BUILTIN); - let x = args[1].as_int().expect("`INT`"); - Ok(range.contains(&x).into()) - }), - _ => None, - }; - } if type1 == type2 { return match op { - "==" => Some(impl_op!(InclusiveRange == InclusiveRange)), - "!=" => Some(impl_op!(InclusiveRange != InclusiveRange)), + Token::EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), + Token::NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), _ => None, }; } @@ -531,8 +496,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, } } else { @@ -544,8 +513,12 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option" | ">=" | "<" | "<=" => Some(|_, _| Ok(Dynamic::FALSE)), + Token::NotEqualsTo => Some(|_, _| Ok(Dynamic::TRUE)), + Token::EqualsTo + | Token::GreaterThan + | Token::GreaterThanEqualsTo + | Token::LessThan + | Token::LessThanEqualsTo => Some(|_, _| Ok(Dynamic::FALSE)), _ => None, }; } @@ -558,7 +531,7 @@ pub fn get_builtin_binary_op_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Option Option { +pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option { let type1 = x.type_id(); let type2 = y.type_id(); @@ -610,49 +583,51 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio #[cfg(not(feature = "unchecked"))] match op { - "+=" => return Some(impl_op!(INT => add(as_int, as_int))), - "-=" => return Some(impl_op!(INT => subtract(as_int, as_int))), - "*=" => return Some(impl_op!(INT => multiply(as_int, as_int))), - "/=" => return Some(impl_op!(INT => divide(as_int, as_int))), - "%=" => return Some(impl_op!(INT => modulo(as_int, as_int))), - "**=" => return Some(impl_op!(INT => power(as_int, as_int))), - ">>=" => return Some(impl_op!(INT => shift_right(as_int, as_int))), - "<<=" => return Some(impl_op!(INT => shift_left(as_int, as_int))), + Token::PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), + Token::MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), + Token::MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), + Token::DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), + Token::ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), + Token::PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), + Token::RightShiftAssign => { + return Some(impl_op!(INT => shift_right(as_int, as_int))) + } + Token::LeftShiftAssign => return Some(impl_op!(INT => shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - "+=" => return Some(impl_op!(INT += as_int)), - "-=" => return Some(impl_op!(INT -= as_int)), - "*=" => return Some(impl_op!(INT *= as_int)), - "/=" => return Some(impl_op!(INT /= as_int)), - "%=" => return Some(impl_op!(INT %= as_int)), - "**=" => return Some(impl_op!(INT => as_int.pow(as_int as u32))), - ">>=" => return Some(impl_op!(INT >>= as_int)), - "<<=" => return Some(impl_op!(INT <<= as_int)), + Token::PlusAssign => return Some(impl_op!(INT += as_int)), + Token::MinusAssign => return Some(impl_op!(INT -= as_int)), + Token::MultiplyAssign => return Some(impl_op!(INT *= as_int)), + Token::DivideAssign => return Some(impl_op!(INT /= as_int)), + Token::ModuloAssign => return Some(impl_op!(INT %= as_int)), + Token::PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + Token::RightShiftAssign => return Some(impl_op!(INT >>= as_int)), + Token::LeftShiftAssign => return Some(impl_op!(INT <<= as_int)), _ => (), } return match op { - "&=" => Some(impl_op!(INT &= as_int)), - "|=" => Some(impl_op!(INT |= as_int)), - "^=" => Some(impl_op!(INT ^= as_int)), + Token::AndAssign => Some(impl_op!(INT &= as_int)), + Token::OrAssign => Some(impl_op!(INT |= as_int)), + Token::XOrAssign => Some(impl_op!(INT ^= as_int)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "&=" => Some(impl_op!(bool = x && as_bool)), - "|=" => Some(impl_op!(bool = x || as_bool)), + Token::AndAssign => Some(impl_op!(bool = x && as_bool)), + Token::OrAssign => Some(impl_op!(bool = x || as_bool)), _ => None, }; } if type1 == TypeId::of::() { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let y = args[1].as_char().expect(BUILTIN); let x = &mut *args[0].write_lock::().expect(BUILTIN); Ok((*x = format!("{x}{y}").into()).into()) @@ -663,13 +638,13 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio if type1 == TypeId::of::() { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); Ok((*x += y).into()) }), - "-=" => Some(|_, args| { + Token::MinusAssign => Some(|_, args| { let (first, second) = args.split_first_mut().expect(BUILTIN); let x = &mut *first.write_lock::().expect(BUILTIN); let y = std::mem::take(second[0]).cast::(); @@ -684,7 +659,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio use crate::Blob; return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let blob2 = std::mem::take(args[1]).cast::(); let blob1 = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::append(blob1, blob2).into()) @@ -699,12 +674,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio ($x:ident, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - "+=" => Some(impl_op!($x += $yy)), - "-=" => Some(impl_op!($x -= $yy)), - "*=" => Some(impl_op!($x *= $yy)), - "/=" => Some(impl_op!($x /= $yy)), - "%=" => Some(impl_op!($x %= $yy)), - "**=" => Some(impl_op!($x => $xx.powf($yy as $x))), + Token::PlusAssign => Some(impl_op!($x += $yy)), + Token::MinusAssign => Some(impl_op!($x -= $yy)), + Token::MultiplyAssign => Some(impl_op!($x *= $yy)), + Token::DivideAssign => Some(impl_op!($x /= $yy)), + Token::ModuloAssign => Some(impl_op!($x %= $yy)), + Token::PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), _ => None, }; } @@ -726,12 +701,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio #[cfg(not(feature = "unchecked"))] return match op { - "+=" => Some(impl_op!(from $x => add($xx, $yy))), - "-=" => Some(impl_op!(from $x => subtract($xx, $yy))), - "*=" => Some(impl_op!(from $x => multiply($xx, $yy))), - "/=" => Some(impl_op!(from $x => divide($xx, $yy))), - "%=" => Some(impl_op!(from $x => modulo($xx, $yy))), - "**=" => Some(impl_op!(from $x => power($xx, $yy))), + Token::PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), + Token::MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), + Token::MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), + Token::DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), + Token::ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), + Token::PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), _ => None, }; @@ -740,12 +715,12 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio #[cfg(feature = "unchecked")] return match op { - "+=" => Some(impl_op!(from $x += $yy)), - "-=" => Some(impl_op!(from $x -= $yy)), - "*=" => Some(impl_op!(from $x *= $yy)), - "/=" => Some(impl_op!(from $x /= $yy)), - "%=" => Some(impl_op!(from $x %= $yy)), - "**=" => Some(impl_op!(from $x => $xx.powd($yy))), + Token::PlusAssign => Some(impl_op!(from $x += $yy)), + Token::MinusAssign => Some(impl_op!(from $x -= $yy)), + Token::MultiplyAssign => Some(impl_op!(from $x *= $yy)), + Token::DivideAssign => Some(impl_op!(from $x /= $yy)), + Token::ModuloAssign => Some(impl_op!(from $x %= $yy)), + Token::PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), _ => None, }; } @@ -761,15 +736,15 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // string op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(impl_op!(ImmutableString += as_char as char)), - "-=" => Some(impl_op!(ImmutableString -= as_char as char)), + Token::PlusAssign => Some(impl_op!(ImmutableString += as_char as char)), + Token::MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), _ => None, }; } // char op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let mut ch = args[0].as_char().expect(BUILTIN).to_string(); ch.push_str( args[1] @@ -793,7 +768,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio if type2 == TypeId::of::() { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let array2 = std::mem::take(args[1]).cast::(); let array1 = &mut *args[0].write_lock::().expect(BUILTIN); Ok(append(array1, array2).into()) @@ -802,7 +777,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio }; } return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let x = std::mem::take(args[1]); let array = &mut *args[0].write_lock::().expect(BUILTIN); Ok(push(array, x).into()) @@ -818,7 +793,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // blob op= int if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let x = args[1].as_int().expect("`INT`"); let blob = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::push(blob, x).into()) @@ -830,7 +805,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // blob op= char if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let x = args[1].as_char().expect("`char`"); let blob = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::append_char(blob, x).into()) @@ -842,7 +817,7 @@ pub fn get_builtin_op_assignment_fn(op: &str, x: &Dynamic, y: &Dynamic) -> Optio // blob op= string if (type1, type2) == (TypeId::of::(), TypeId::of::()) { return match op { - "+=" => Some(|_, args| { + Token::PlusAssign => Some(|_, args| { let s = std::mem::take(args[1]).cast::(); let blob = &mut *args[0].write_lock::().expect(BUILTIN); Ok(crate::packages::blob_basic::blob_functions::append_str(blob, &s).into()) diff --git a/src/func/call.rs b/src/func/call.rs index e91d63a8..ea6349fe 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -9,6 +9,7 @@ use crate::engine::{ KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; +use crate::tokenizer::Token; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, Module, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, @@ -191,7 +192,7 @@ impl Engine { hash_base: u64, args: Option<&mut FnCallArgs>, allow_dynamic: bool, - is_op_assignment: bool, + op_assignment_token: Option<&Token>, ) -> Option<&'s FnResolutionCacheEntry> { if hash_base == 0 { return None; @@ -279,22 +280,23 @@ impl Engine { // Try to find a built-in version let builtin = args.and_then(|args| { - if is_op_assignment { + if let Some(op_assign) = op_assignment_token { let (first_arg, rest_args) = args.split_first().unwrap(); - get_builtin_op_assignment_fn(fn_name, *first_arg, rest_args[0]).map( - |f| FnResolutionCacheEntry { + get_builtin_op_assignment_fn(op_assign, *first_arg, rest_args[0]) + .map(|f| FnResolutionCacheEntry { func: CallableFunction::from_fn_builtin(f), source: None, - }, - ) - } else { - get_builtin_binary_op_fn(fn_name, args[0], args[1]).map(|f| { + }) + } else if let Some(ref operator) = Token::lookup_from_syntax(fn_name) { + get_builtin_binary_op_fn(operator, args[0], args[1]).map(|f| { FnResolutionCacheEntry { func: CallableFunction::from_fn_builtin(f), source: None, } }) + } else { + None } }); @@ -360,6 +362,11 @@ impl Engine { self.inc_operations(&mut global.num_operations, pos)?; let parent_source = global.source.clone(); + let op_assign = if is_op_assign { + Token::lookup_from_syntax(name) + } else { + None + }; // Check if function access already in the cache let local_entry = &mut None; @@ -373,7 +380,7 @@ impl Engine { hash, Some(args), true, - is_op_assign, + op_assign.as_ref(), ); if func.is_some() { @@ -653,7 +660,7 @@ impl Engine { hashes.script, None, false, - false, + None, ) .cloned() { @@ -993,7 +1000,7 @@ impl Engine { args_expr: &[Expr], hashes: FnCallHashes, capture_scope: bool, - is_operator: bool, + operator_token: Option<&Token>, pos: Position, level: usize, ) -> RhaiResult { @@ -1006,7 +1013,7 @@ impl Engine { let redirected; // Handle call() - Redirect function call match name { - _ if is_operator => (), + _ if operator_token.is_some() => (), // Handle call() KEYWORD_FN_PTR_CALL if total_args >= 1 => { diff --git a/src/optimizer.rs b/src/optimizer.rs index 216e4f3c..6ab19fcb 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1181,8 +1181,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { - if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1]) + _ if x.args.len() == 2 && x.operator_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native, &arg_types)) => { + if let Some(result) = get_builtin_binary_op_fn(x.operator_token.as_ref().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] let lib = state.lib; diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index a3bbd532..4b6639c2 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -146,6 +146,21 @@ pub mod blob_functions { pub fn is_empty(blob: &mut Blob) -> bool { blob.len() == 0 } + /// Return `true` if the BLOB contains a specified byte value. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.contains('h')); // prints true + /// + /// print(text.contains('x')); // prints false + /// ``` + #[rhai_fn(name = "contains")] + pub fn contains(blob: &mut Blob, value: INT) -> bool { + blob.contains(&((value & 0x0000_00ff) as u8)) + } /// Get the byte value at the `index` position in the BLOB. /// /// * If `index` < 0, position counts from the end of the BLOB (`-1` is the last element). diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index 6ec293cc..b9c849fe 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -668,6 +668,12 @@ mod range_functions { pub fn is_empty_exclusive(range: &mut ExclusiveRange) -> bool { range.is_empty() } + /// Return `true` if the range contains a specified value. + #[rhai_fn(name = "contains")] + pub fn contains_exclusive(range: &mut ExclusiveRange, value: INT) -> bool { + range.contains(&value) + } + /// Return the start of the inclusive range. #[rhai_fn(get = "start", name = "start", pure)] pub fn start_inclusive(range: &mut InclusiveRange) -> INT { @@ -695,4 +701,9 @@ mod range_functions { pub fn is_empty_inclusive(range: &mut InclusiveRange) -> bool { range.is_empty() } + /// Return `true` if the range contains a specified value. + #[rhai_fn(name = "contains")] + pub fn contains_inclusive(range: &mut InclusiveRange, value: INT) -> bool { + range.contains(&value) + } } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index 09ffdce6..955b8395 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -30,6 +30,20 @@ mod map_functions { pub fn is_empty(map: &mut Map) -> bool { map.len() == 0 } + /// Returns `true` if the object map contains a specified property. + /// + /// # Example + /// + /// ```rhai + /// let m = #{a: 1, b: 2, c: 3}; + /// + /// print(m.contains("b")); // prints true + /// + /// print(m.contains("x")); // prints false + /// ``` + pub fn contains(map: &mut Map, property: &str) -> bool { + map.contains_key(property) + } /// Get the value of the `property` in the object map and return a copy. /// /// If `property` does not exist in the object map, `()` is returned. diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index da53a127..93e8edf4 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -506,6 +506,37 @@ mod string_functions { *character = to_lower_char(*character); } + /// Return `true` if the string contains a specified string. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.contains("hello")); // prints true + /// + /// print(text.contains("hey")); // prints false + /// ``` + pub fn contains(string: &str, match_string: &str) -> bool { + string.contains(match_string) + } + + /// Return `true` if the string contains a specified character. + /// + /// # Example + /// + /// ```rhai + /// let text = "hello, world!"; + /// + /// print(text.contains('h')); // prints true + /// + /// print(text.contains('x')); // prints false + /// ``` + #[rhai_fn(name = "contains")] + pub fn contains_char(string: &str, character: char) -> bool { + string.contains(character).into() + } + /// Return `true` if the string starts with a specified string. /// /// # Example diff --git a/src/parser.rs b/src/parser.rs index 621a1b47..9941fc53 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -472,7 +472,7 @@ fn match_token(input: &mut TokenStream, token: Token) -> (bool, Position) { fn parse_var_name(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> { match input.next().expect(NEVER_ENDS) { // Variable name - (Token::Identifier(s), pos) => Ok((s, pos)), + (Token::Identifier(s), pos) => Ok((*s, pos)), // Reserved keyword (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { Err(PERR::Reserved(s.to_string()).into_err(pos)) @@ -492,7 +492,7 @@ fn parse_symbol(input: &mut TokenStream) -> ParseResult<(SmartString, Position)> // Symbol (token, pos) if token.is_standard_symbol() => Ok((token.literal_syntax().into(), pos)), // Reserved symbol - (Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((s, pos)), + (Token::Reserved(s), pos) if !is_valid_identifier(s.chars()) => Ok((*s, pos)), // Bad symbol (Token::LexError(err), pos) => Err(err.into_err(pos)), // Not a symbol @@ -616,7 +616,7 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - is_native_operator: false, + operator_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -684,7 +684,7 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - is_native_operator: false, + operator_token: None, #[cfg(not(feature = "no_module"))] namespace, hashes, @@ -1000,10 +1000,10 @@ impl Engine { let (name, pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s) | Token::StringConstant(s), pos) => { - if map.iter().any(|(p, ..)| **p == s) { + if map.iter().any(|(p, ..)| **p == *s) { return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); } - (s, pos) + (*s, pos) } (Token::InterpolatedString(..), pos) => { return Err(PERR::PropertyExpected.into_err(pos)) @@ -1342,7 +1342,7 @@ impl Engine { Token::IntegerConstant(x) => Expr::IntegerConstant(x, settings.pos), Token::CharConstant(c) => Expr::CharConstant(c, settings.pos), Token::StringConstant(s) => { - Expr::StringConstant(state.get_interned_string(s), settings.pos) + Expr::StringConstant(state.get_interned_string(*s), settings.pos) } Token::True => Expr::BoolConstant(true, settings.pos), Token::False => Expr::BoolConstant(false, settings.pos), @@ -1356,7 +1356,7 @@ impl Engine { } #[cfg(feature = "decimal")] Token::DecimalConstant(x) => { - let x = (*x).into(); + let x = (**x).into(); input.next(); Expr::DynamicConstant(Box::new(x), settings.pos) } @@ -1479,7 +1479,7 @@ impl Engine { match input.next().expect(NEVER_ENDS) { (Token::InterpolatedString(s), ..) if s.is_empty() => (), (Token::InterpolatedString(s), pos) => { - segments.push(Expr::StringConstant(s.into(), pos)) + segments.push(Expr::StringConstant(state.get_interned_string(*s), pos)) } token => { unreachable!("Token::InterpolatedString expected but gets {:?}", token) @@ -1502,14 +1502,16 @@ impl Engine { match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { if !s.is_empty() { - segments.push(Expr::StringConstant(s.into(), pos)); + segments + .push(Expr::StringConstant(state.get_interned_string(*s), pos)); } // End the interpolated string if it is terminated by a back-tick. break; } (Token::InterpolatedString(s), pos) => { if !s.is_empty() { - segments.push(Expr::StringConstant(s.into(), pos)); + segments + .push(Expr::StringConstant(state.get_interned_string(*s), pos)); } } (Token::LexError(err), pos) @@ -1574,7 +1576,7 @@ impl Engine { state.allow_capture = true; } Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), + (None, ns, 0, state.get_interned_string(*s)).into(), None, settings.pos, ) @@ -1587,7 +1589,7 @@ impl Engine { // Once the identifier consumed we must enable next variables capturing state.allow_capture = true; } - let name = state.get_interned_string(s); + let name = state.get_interned_string(*s); Expr::Variable((None, ns, 0, name).into(), None, settings.pos) } // Normal variable access @@ -1612,7 +1614,7 @@ impl Engine { None } }); - let name = state.get_interned_string(s); + let name = state.get_interned_string(*s); Expr::Variable((index, ns, 0, name).into(), short_index, settings.pos) } } @@ -1634,7 +1636,7 @@ impl Engine { // Function call is allowed to have reserved keyword Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => { Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), + (None, ns, 0, state.get_interned_string(*s)).into(), None, settings.pos, ) @@ -1642,7 +1644,7 @@ impl Engine { // Access to `this` as a variable is OK within a function scope #[cfg(not(feature = "no_function"))] _ if &*s == KEYWORD_THIS && settings.in_fn_scope => Expr::Variable( - (None, ns, 0, state.get_interned_string(s)).into(), + (None, ns, 0, state.get_interned_string(*s)).into(), None, settings.pos, ), @@ -1888,7 +1890,7 @@ impl Engine { // -expr Token::Minus | Token::UnaryMinus => { let token = token.clone(); - let pos = eat_token(input, token); + let pos = eat_token(input, token.clone()); match self.parse_unary(input, state, lib, settings.level_up())? { // Negative integer @@ -1918,7 +1920,7 @@ impl Engine { hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), args, pos, - is_native_operator: true, + operator_token: Some(token), ..Default::default() } .into_fn_call_expr(pos)) @@ -1928,7 +1930,7 @@ impl Engine { // +expr Token::Plus | Token::UnaryPlus => { let token = token.clone(); - let pos = eat_token(input, token); + let pos = eat_token(input, token.clone()); match self.parse_unary(input, state, lib, settings.level_up())? { expr @ Expr::IntegerConstant(..) => Ok(expr), @@ -1946,7 +1948,7 @@ impl Engine { hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), args, pos, - is_native_operator: true, + operator_token: Some(token), ..Default::default() } .into_fn_call_expr(pos)) @@ -1955,7 +1957,9 @@ impl Engine { } // !expr Token::Bang => { + let token = token.clone(); let pos = eat_token(input, Token::Bang); + let mut args = StaticVec::new_const(); args.push(self.parse_unary(input, state, lib, settings.level_up())?); args.shrink_to_fit(); @@ -1965,7 +1969,7 @@ impl Engine { hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), args, pos, - is_native_operator: true, + operator_token: Some(token), ..Default::default() } .into_fn_call_expr(pos)) @@ -2283,7 +2287,7 @@ impl Engine { #[cfg(not(feature = "no_custom_syntax"))] Token::Custom(c) => self .custom_keywords - .get(c) + .get(&**c) .copied() .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?, Token::Reserved(c) if !is_valid_identifier(c.chars()) => { @@ -2308,7 +2312,7 @@ impl Engine { #[cfg(not(feature = "no_custom_syntax"))] Token::Custom(c) => self .custom_keywords - .get(c) + .get(&**c) .copied() .ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?, Token::Reserved(c) if !is_valid_identifier(c.chars()) => { @@ -2335,12 +2339,17 @@ impl Engine { let op = op_token.syntax(); let hash = calc_fn_hash(None, &op, 2); + let operator_token = if is_valid_function_name(&op) { + None + } else { + Some(op_token.clone()) + }; let op_base = FnCallExpr { name: state.get_interned_string(op.as_ref()), hashes: FnCallHashes::from_native(hash), pos, - is_native_operator: !is_valid_function_name(&op), + operator_token, ..Default::default() }; @@ -2578,7 +2587,7 @@ impl Engine { }, CUSTOM_SYNTAX_MARKER_STRING => match input.next().expect(NEVER_ENDS) { (Token::StringConstant(s), pos) => { - let s = state.get_interned_string(s); + let s = state.get_interned_string(*s); inputs.push(Expr::StringConstant(s.clone(), pos)); segments.push(s); tokens.push(state.get_interned_string(CUSTOM_SYNTAX_MARKER_STRING)); @@ -3228,7 +3237,7 @@ impl Engine { match input.next().expect(NEVER_ENDS).0 { Token::Comment(comment) => { - comments.push(comment); + comments.push(*comment); match input.peek().expect(NEVER_ENDS) { (Token::Fn | Token::Private, ..) => break, @@ -3562,7 +3571,7 @@ impl Engine { return Err(PERR::FnDuplicatedParam(name.to_string(), s.to_string()) .into_err(pos)); } - let s = state.get_interned_string(s); + let s = state.get_interned_string(*s); state.stack.push(s.clone(), ()); params.push((s, pos)); } @@ -3700,7 +3709,7 @@ impl Engine { PERR::FnDuplicatedParam(String::new(), s.to_string()).into_err(pos) ); } - let s = state.get_interned_string(s); + let s = state.get_interned_string(*s); state.stack.push(s.clone(), ()); params_list.push(s); } diff --git a/src/tests.rs b/src/tests.rs index 1d897871..c91a1e1e 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -22,15 +22,7 @@ fn check_struct_sizes() { ); assert_eq!( size_of::(), - if IS_32_BIT { - if cfg!(feature = "decimal") { - 24 - } else { - 16 - } - } else { - 32 - } + if IS_32_BIT { 8 } else { 16 } ); assert_eq!(size_of::(), if PACKED { 12 } else { 16 }); assert_eq!(size_of::>(), if PACKED { 12 } else { 16 }); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6cc7eb57..e3d615e8 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -381,15 +381,15 @@ pub enum Token { /// /// Requires the `decimal` feature. #[cfg(feature = "decimal")] - DecimalConstant(rust_decimal::Decimal), + DecimalConstant(Box), /// An identifier. - Identifier(Identifier), + Identifier(Box), /// A character constant. CharConstant(char), /// A string constant. - StringConstant(SmartString), + StringConstant(Box), /// An interpolated string. - InterpolatedString(SmartString), + InterpolatedString(Box), /// `{` LeftBrace, /// `}` @@ -570,14 +570,14 @@ pub enum Token { /// A lexer error. LexError(Box), /// A comment block. - Comment(SmartString), + Comment(Box), /// A reserved symbol. - Reserved(SmartString), + Reserved(Box), /// A custom keyword. /// /// Not available under `no_custom_syntax`. #[cfg(not(feature = "no_custom_syntax"))] - Custom(SmartString), + Custom(Box), /// End of the input stream. EOF, } @@ -898,21 +898,23 @@ impl Token { // List of reserved operators "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" - | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => Reserved(syntax.into()), + | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => { + Reserved(Box::new(syntax.into())) + } // List of reserved keywords "public" | "protected" | "super" | "new" | "use" | "module" | "package" | "var" | "static" | "shared" | "with" | "is" | "goto" | "exit" | "match" | "case" | "default" | "void" | "null" | "nil" | "spawn" | "thread" | "go" | "sync" - | "async" | "await" | "yield" => Reserved(syntax.into()), + | "async" | "await" | "yield" => Reserved(Box::new(syntax.into())), KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_THIS | KEYWORD_IS_DEF_VAR => { - Reserved(syntax.into()) + Reserved(Box::new(syntax.into())) } #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN => Reserved(syntax.into()), + crate::engine::KEYWORD_IS_DEF_FN => Reserved(Box::new(syntax.into())), _ => return None, }) @@ -1097,8 +1099,8 @@ impl Token { pub(crate) fn into_function_name_for_override(self) -> Result { match self { #[cfg(not(feature = "no_custom_syntax"))] - Self::Custom(s) if is_valid_function_name(&s) => Ok(s), - Self::Identifier(s) if is_valid_function_name(&s) => Ok(s), + Self::Custom(s) if is_valid_function_name(&s) => Ok(*s), + Self::Identifier(s) if is_valid_function_name(&s) => Ok(*s), _ => Err(self), } } @@ -1510,7 +1512,7 @@ fn get_next_token_inner( let return_comment = return_comment || is_doc_comment(comment.as_ref().expect("`Some`")); if return_comment { - return Some((Token::Comment(comment.expect("`Some`")), start_pos)); + return Some((Token::Comment(comment.expect("`Some`").into()), start_pos)); } if state.comment_level > 0 { // Reached EOF without ending comment block @@ -1524,9 +1526,9 @@ fn get_next_token_inner( |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(result, interpolated, start_pos)| { if interpolated { - Some((Token::InterpolatedString(result), start_pos)) + Some((Token::InterpolatedString(result.into()), start_pos)) } else { - Some((Token::StringConstant(result), start_pos)) + Some((Token::StringConstant(result.into()), start_pos)) } }, ); @@ -1676,13 +1678,16 @@ fn get_next_token_inner( // Then try decimal #[cfg(feature = "decimal")] let num = num.or_else(|_| { - rust_decimal::Decimal::from_str(&result).map(Token::DecimalConstant) + rust_decimal::Decimal::from_str(&result) + .map(Box::new) + .map(Token::DecimalConstant) }); // Then try decimal in scientific notation #[cfg(feature = "decimal")] let num = num.or_else(|_| { rust_decimal::Decimal::from_scientific(&result) + .map(Box::new) .map(Token::DecimalConstant) }); @@ -1709,7 +1714,7 @@ fn get_next_token_inner( return parse_string_literal(stream, state, pos, c, false, true, false) .map_or_else( |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), - |(result, ..)| Some((Token::StringConstant(result), start_pos)), + |(result, ..)| Some((Token::StringConstant(result.into()), start_pos)), ); } // ` - string literal @@ -1737,9 +1742,9 @@ fn get_next_token_inner( |(err, err_pos)| Some((Token::LexError(err.into()), err_pos)), |(result, interpolated, ..)| { if interpolated { - Some((Token::InterpolatedString(result), start_pos)) + Some((Token::InterpolatedString(result.into()), start_pos)) } else { - Some((Token::StringConstant(result), start_pos)) + Some((Token::StringConstant(result.into()), start_pos)) } }, ); @@ -1786,7 +1791,7 @@ fn get_next_token_inner( // Parentheses ('(', '*') => { eat_next(stream, pos); - return Some((Token::Reserved("(*".into()), start_pos)); + return Some((Token::Reserved(Box::new("(*".into())), start_pos)); } ('(', ..) => return Some((Token::LeftParen, start_pos)), (')', ..) => return Some((Token::RightParen, start_pos)), @@ -1802,7 +1807,7 @@ fn get_next_token_inner( return Some((Token::MapStart, start_pos)); } // Shebang - ('#', '!') => return Some((Token::Reserved("#!".into()), start_pos)), + ('#', '!') => return Some((Token::Reserved(Box::new("#!".into())), start_pos)), ('#', ' ') => { eat_next(stream, pos); @@ -1812,10 +1817,10 @@ fn get_next_token_inner( } else { "#" }; - return Some((Token::Reserved(token.into()), start_pos)); + return Some((Token::Reserved(Box::new(token.into())), start_pos)); } - ('#', ..) => return Some((Token::Reserved("#".into()), start_pos)), + ('#', ..) => return Some((Token::Reserved(Box::new("#".into())), start_pos)), // Operators ('+', '=') => { @@ -1824,7 +1829,7 @@ fn get_next_token_inner( } ('+', '+') => { eat_next(stream, pos); - return Some((Token::Reserved("++".into()), start_pos)); + return Some((Token::Reserved(Box::new("++".into())), start_pos)); } ('+', ..) if !state.next_token_cannot_be_unary => { return Some((Token::UnaryPlus, start_pos)) @@ -1839,11 +1844,11 @@ fn get_next_token_inner( } ('-', '>') => { eat_next(stream, pos); - return Some((Token::Reserved("->".into()), start_pos)); + return Some((Token::Reserved(Box::new("->".into())), start_pos)); } ('-', '-') => { eat_next(stream, pos); - return Some((Token::Reserved("--".into()), start_pos)); + return Some((Token::Reserved(Box::new("--".into())), start_pos)); } ('-', ..) if !state.next_token_cannot_be_unary => { return Some((Token::UnaryMinus, start_pos)) @@ -1852,7 +1857,7 @@ fn get_next_token_inner( ('*', ')') => { eat_next(stream, pos); - return Some((Token::Reserved("*)".into()), start_pos)); + return Some((Token::Reserved(Box::new("*)".into())), start_pos)); } ('*', '=') => { eat_next(stream, pos); @@ -1925,7 +1930,7 @@ fn get_next_token_inner( .borrow_mut() .global_comments .push(comment), - _ => return Some((Token::Comment(comment), start_pos)), + _ => return Some((Token::Comment(comment.into()), start_pos)), } } } @@ -1953,7 +1958,7 @@ fn get_next_token_inner( scan_block_comment(stream, state.comment_level, pos, comment.as_mut()); if let Some(comment) = comment { - return Some((Token::Comment(comment), start_pos)); + return Some((Token::Comment(comment.into()), start_pos)); } } @@ -1972,7 +1977,7 @@ fn get_next_token_inner( match stream.peek_next() { Some('.') => { eat_next(stream, pos); - Token::Reserved("...".into()) + Token::Reserved(Box::new("...".into())) } Some('=') => { eat_next(stream, pos); @@ -1990,7 +1995,7 @@ fn get_next_token_inner( if stream.peek_next() == Some('=') { eat_next(stream, pos); - return Some((Token::Reserved("===".into()), start_pos)); + return Some((Token::Reserved(Box::new("===".into())), start_pos)); } return Some((Token::EqualsTo, start_pos)); @@ -2007,18 +2012,18 @@ fn get_next_token_inner( if stream.peek_next() == Some('<') { eat_next(stream, pos); - return Some((Token::Reserved("::<".into()), start_pos)); + return Some((Token::Reserved(Box::new("::<".into())), start_pos)); } return Some((Token::DoubleColon, start_pos)); } (':', '=') => { eat_next(stream, pos); - return Some((Token::Reserved(":=".into()), start_pos)); + return Some((Token::Reserved(Box::new(":=".into())), start_pos)); } (':', ';') => { eat_next(stream, pos); - return Some((Token::Reserved(":;".into()), start_pos)); + return Some((Token::Reserved(Box::new(":;".into())), start_pos)); } (':', ..) => return Some((Token::Colon, start_pos)), @@ -2028,7 +2033,7 @@ fn get_next_token_inner( } ('<', '-') => { eat_next(stream, pos); - return Some((Token::Reserved("<-".into()), start_pos)); + return Some((Token::Reserved(Box::new("<-".into())), start_pos)); } ('<', '<') => { eat_next(stream, pos); @@ -2045,7 +2050,7 @@ fn get_next_token_inner( } ('<', '|') => { eat_next(stream, pos); - return Some((Token::Reserved("<|".into()), start_pos)); + return Some((Token::Reserved(Box::new("<|".into())), start_pos)); } ('<', ..) => return Some((Token::LessThan, start_pos)), @@ -2073,14 +2078,14 @@ fn get_next_token_inner( if stream.peek_next() == Some('=') { eat_next(stream, pos); - return Some((Token::Reserved("!==".into()), start_pos)); + return Some((Token::Reserved(Box::new("!==".into())), start_pos)); } return Some((Token::NotEqualsTo, start_pos)); } ('!', '.') => { eat_next(stream, pos); - return Some((Token::Reserved("!.".into()), start_pos)); + return Some((Token::Reserved(Box::new("!.".into())), start_pos)); } ('!', ..) => return Some((Token::Bang, start_pos)), @@ -2094,7 +2099,7 @@ fn get_next_token_inner( } ('|', '>') => { eat_next(stream, pos); - return Some((Token::Reserved("|>".into()), start_pos)); + return Some((Token::Reserved(Box::new("|>".into())), start_pos)); } ('|', ..) => return Some((Token::Pipe, start_pos)), @@ -2114,7 +2119,7 @@ fn get_next_token_inner( } ('^', ..) => return Some((Token::XOr, start_pos)), - ('~', ..) => return Some((Token::Reserved("~".into()), start_pos)), + ('~', ..) => return Some((Token::Reserved(Box::new("~".into())), start_pos)), ('%', '=') => { eat_next(stream, pos); @@ -2122,9 +2127,9 @@ fn get_next_token_inner( } ('%', ..) => return Some((Token::Modulo, start_pos)), - ('@', ..) => return Some((Token::Reserved("@".into()), start_pos)), + ('@', ..) => return Some((Token::Reserved(Box::new("@".into())), start_pos)), - ('$', ..) => return Some((Token::Reserved("$".into()), start_pos)), + ('$', ..) => return Some((Token::Reserved(Box::new("$".into())), start_pos)), ('?', '.') => { eat_next(stream, pos); @@ -2132,7 +2137,7 @@ fn get_next_token_inner( #[cfg(not(feature = "no_object"))] Token::Elvis, #[cfg(feature = "no_object")] - Token::Reserved("?.".into()), + Token::Reserved(Box::new("?.".into())), start_pos, )); } @@ -2146,11 +2151,11 @@ fn get_next_token_inner( #[cfg(not(feature = "no_index"))] Token::QuestionBracket, #[cfg(feature = "no_index")] - Token::Reserved("?[".into()), + Token::Reserved(Box::new("?[".into())), start_pos, )); } - ('?', ..) => return Some((Token::Reserved("?".into()), start_pos)), + ('?', ..) => return Some((Token::Reserved(Box::new("?".into())), start_pos)), (ch, ..) if ch.is_whitespace() => (), @@ -2201,7 +2206,7 @@ fn get_identifier( ); } - (Token::Identifier(identifier), start_pos) + (Token::Identifier(identifier.into()), start_pos) } /// Is a keyword allowed as a function? @@ -2382,7 +2387,7 @@ impl<'a> Iterator for TokenIterator<'a> { } // Reserved keyword/symbol Some((Token::Reserved(s), pos)) => (match - (&*s, + (s.as_str(), #[cfg(not(feature = "no_custom_syntax"))] (!self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(&*s)), #[cfg(feature = "no_custom_syntax")] @@ -2438,7 +2443,7 @@ impl<'a> Iterator for TokenIterator<'a> { Some((token, pos)) if !self.engine.custom_keywords.is_empty() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => { if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) { // Disabled standard keyword/symbol - (Token::Custom(token.literal_syntax().into()), pos) + (Token::Custom(Box::new(token.literal_syntax().into())), pos) } else { // Active standard keyword - should never be a custom keyword! unreachable!("{:?} is an active keyword", token) @@ -2446,7 +2451,7 @@ impl<'a> Iterator for TokenIterator<'a> { } // Disabled symbol Some((token, pos)) if !self.engine.disabled_symbols.is_empty() && self.engine.disabled_symbols.contains(token.literal_syntax()) => { - (Token::Reserved(token.literal_syntax().into()), pos) + (Token::Reserved(Box::new(token.literal_syntax().into())), pos) } // Normal symbol Some(r) => r,