From 52d23522e672c722fe45b70bd79c486904f75501 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 25 Dec 2022 12:44:49 +0800 Subject: [PATCH 1/7] Add for_each for arrays. --- CHANGELOG.md | 5 +- src/packages/array_basic.rs | 70 +++++++++++++++++----- src/types/fn_ptr.rs | 114 +++++++++++++++++++++++++----------- tests/arrays.rs | 45 ++++++++++---- 4 files changed, 172 insertions(+), 62 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 72776dd3..a81820de 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -57,8 +57,9 @@ Net features ### Enhanced array API -* Array methods that take a function pointer, usually a closure (e.g. `map`, `filter`, `index_of` etc.), can now provide a closure with one few parameter but binds the first parameter to `this`. -* This vastly improves performance when working with arrays of object maps by avoiding unnecessary cloning of large types. +* Array methods that take a function pointer, usually a closure (e.g. `map`, `filter`, `index_of`, `reduce` etc.), can now bind the array element to `this` when calling a closure. +* This vastly improves performance when working with arrays of large types (e.g. object maps) by avoiding unnecessary cloning. +* `for_each` is also added for arrays, allowing a closure to mutate array elements (bound to `this`) in turn. Enhancements ------------ diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 51af68a0..c1502d4a 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -644,6 +644,43 @@ pub mod array_functions { result } } + + /// Iterate through all the elements in the array, applying a `process` function to each element in turn. + /// Each element is bound to `this` before calling the function. + /// + /// # Function Parameters + /// + /// * `this`: bound to array element (mutable) + /// * `index` _(optional)_: current index in the array + /// + /// # Example + /// + /// ```rhai + /// let x = [1, 2, 3, 4, 5]; + /// + /// x.for_each(|| this *= this); + /// + /// print(x); // prints "[1, 4, 9, 16, 25]" + /// + /// x.for_each(|i| this *= i); + /// + /// print(x); // prints "[0, 2, 6, 12, 20]" + /// ``` + #[rhai_fn(return_raw)] + pub fn for_each(ctx: NativeCallContext, array: &mut Array, map: FnPtr) -> RhaiResultOf<()> { + if array.is_empty() { + return Ok(()); + } + + for (i, item) in array.iter_mut().enumerate() { + let ex = [(i as INT).into()]; + + let _ = map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, None)?; + } + + Ok(()) + } + /// Iterate through all the elements in the array, applying a `mapper` function to each element /// in turn, and return the results as a new array. /// @@ -679,8 +716,7 @@ pub mod array_functions { for (i, item) in array.iter_mut().enumerate() { let ex = [(i as INT).into()]; - - ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex)?); + ar.push(map.call_raw_with_extra_args("map", &ctx, Some(item), [], ex, Some(0))?); } Ok(ar) @@ -723,7 +759,7 @@ pub mod array_functions { let ex = [(i as INT).into()]; if filter - .call_raw_with_extra_args("filter", &ctx, Some(item), [], ex)? + .call_raw_with_extra_args("filter", &ctx, Some(item), [], ex, Some(0))? .as_bool() .unwrap_or(false) { @@ -962,7 +998,7 @@ pub mod array_functions { let ex = [(i as INT).into()]; if filter - .call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex)? + .call_raw_with_extra_args("index_of", &ctx, Some(item), [], ex, Some(0))? .as_bool() .unwrap_or(false) { @@ -1128,7 +1164,8 @@ pub mod array_functions { for (i, item) in array.iter_mut().enumerate().skip(start) { let ex = [(i as INT).into()]; - let value = filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex)?; + let value = + filter.call_raw_with_extra_args("find_map", &ctx, Some(item), [], ex, Some(0))?; if !value.is_unit() { return Ok(value); @@ -1169,7 +1206,7 @@ pub mod array_functions { let ex = [(i as INT).into()]; if filter - .call_raw_with_extra_args("some", &ctx, Some(item), [], ex)? + .call_raw_with_extra_args("some", &ctx, Some(item), [], ex, Some(0))? .as_bool() .unwrap_or(false) { @@ -1211,7 +1248,7 @@ pub mod array_functions { let ex = [(i as INT).into()]; if !filter - .call_raw_with_extra_args("all", &ctx, Some(item), [], ex)? + .call_raw_with_extra_args("all", &ctx, Some(item), [], ex, Some(0))? .as_bool() .unwrap_or(false) { @@ -1334,11 +1371,11 @@ pub mod array_functions { } array - .iter() + .iter_mut() .enumerate() .try_fold(initial, |result, (i, item)| { let ex = [(i as INT).into()]; - reducer.call_raw_with_extra_args("reduce", &ctx, None, [result, item.clone()], ex) + reducer.call_raw_with_extra_args("reduce", &ctx, Some(item), [result], ex, Some(1)) }) } /// Reduce an array by iterating through all elements, in _reverse_ order, @@ -1400,19 +1437,22 @@ pub mod array_functions { return Ok(initial); } + let len = array.len(); + array - .iter() + .iter_mut() .rev() .enumerate() .try_fold(initial, |result, (i, item)| { - let ex = [((array.len() - 1 - i) as INT).into()]; + let ex = [((len - 1 - i) as INT).into()]; reducer.call_raw_with_extra_args( "reduce_rev", &ctx, - None, - [result, item.clone()], + Some(item), + [result], ex, + Some(1), ) }) } @@ -1602,7 +1642,7 @@ pub mod array_functions { let ex = [(i as INT).into()]; if filter - .call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex)? + .call_raw_with_extra_args("drain", &ctx, Some(&mut array[x]), [], ex, Some(0))? .as_bool() .unwrap_or(false) { @@ -1749,7 +1789,7 @@ pub mod array_functions { let ex = [(i as INT).into()]; if filter - .call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex)? + .call_raw_with_extra_args("retain", &ctx, Some(&mut array[x]), [], ex, Some(0))? .as_bool() .unwrap_or(false) { diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 952a402c..ca678840 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -360,7 +360,9 @@ impl FnPtr { /// arguments attached. /// /// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`. - /// When an appropriate function is not found, it is then removed and mapped to the first parameter. + /// + /// When an appropriate function is not found and `move_this_ptr_to_args` is `Some`, `this_ptr` + /// is removed and inserted as the appropriate parameter number. /// /// This is useful for calling predicate closures within an iteration loop where the extra argument /// is the current element's index. @@ -374,17 +376,19 @@ impl FnPtr { fn_name: &str, ctx: &NativeCallContext, this_ptr: Option<&mut Dynamic>, - items: [Dynamic; N], + args: [Dynamic; N], extras: [Dynamic; E], + move_this_ptr_to_args: Option, ) -> RhaiResult { - self._call_with_extra_args(fn_name, ctx, this_ptr, items, extras) + self._call_with_extra_args(fn_name, ctx, this_ptr, args, extras, move_this_ptr_to_args) } /// _(internals)_ Make a call to a function pointer with either a specified number of arguments, - /// or with extra arguments attached. - /// Exported under the `internals` feature only. + /// or with extra arguments attached. Exported under the `internals` feature only. /// /// If `this_ptr` is provided, it is first provided to script-defined functions bound to `this`. - /// When an appropriate function is not found, it is then removed and mapped to the first parameter. + /// + /// When an appropriate function is not found and `move_this_ptr_to_args` is `Some`, `this_ptr` + /// is removed and inserted as the appropriate parameter number. /// /// This is useful for calling predicate closures within an iteration loop where the extra /// argument is the current element's index. @@ -398,10 +402,11 @@ impl FnPtr { fn_name: &str, ctx: &NativeCallContext, this_ptr: Option<&mut Dynamic>, - items: [Dynamic; N], + args: [Dynamic; N], extras: [Dynamic; E], + move_this_ptr_to_args: Option, ) -> RhaiResult { - self._call_with_extra_args(fn_name, ctx, this_ptr, items, extras) + self._call_with_extra_args(fn_name, ctx, this_ptr, args, extras, move_this_ptr_to_args) } /// Make a call to a function pointer with either a specified number of arguments, or with extra /// arguments attached. @@ -410,51 +415,94 @@ impl FnPtr { fn_name: &str, ctx: &NativeCallContext, mut this_ptr: Option<&mut Dynamic>, - items: [Dynamic; N], + args: [Dynamic; N], extras: [Dynamic; E], + move_this_ptr_to_args: Option, ) -> RhaiResult { #[cfg(not(feature = "no_function"))] if let Some(arity) = self.fn_def().map(|f| f.params.len()) { - if arity == N + 1 && this_ptr.is_some() { - let mut args = FnArgsVec::with_capacity(items.len() + 1); - args.push(this_ptr.as_mut().unwrap().clone()); - args.extend(items); - return self.call_raw(ctx, None, args); + if let Some(move_to_args) = move_this_ptr_to_args { + if this_ptr.is_some() { + if arity == N + 1 { + let mut args2 = FnArgsVec::with_capacity(args.len() + 1); + if move_to_args == 0 { + args2.push(this_ptr.as_mut().unwrap().clone()); + args2.extend(args); + } else { + args2.extend(args); + args2.insert(move_to_args, this_ptr.as_mut().unwrap().clone()); + } + return self.call_raw(ctx, None, args2); + } + if arity == N + E + 1 { + let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1); + if move_to_args == 0 { + args2.push(this_ptr.as_mut().unwrap().clone()); + args2.extend(args); + args2.extend(extras); + } else { + args2.extend(args); + args2.insert(move_to_args, this_ptr.as_mut().unwrap().clone()); + args2.extend(extras); + } + return self.call_raw(ctx, None, args2); + } + } } if arity == N { - return self.call_raw(ctx, this_ptr, items); + return self.call_raw(ctx, this_ptr, args); } if arity == N + E { - let mut items2 = FnArgsVec::with_capacity(items.len() + extras.len()); - items2.extend(IntoIterator::into_iter(items)); - items2.extend(IntoIterator::into_iter(extras)); - return self.call_raw(ctx, this_ptr, items2); + let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len()); + args2.extend(args); + args2.extend(extras); + return self.call_raw(ctx, this_ptr, args2); } } - self.call_raw(ctx, this_ptr.as_deref_mut(), items.clone()) + self.call_raw(ctx, this_ptr.as_deref_mut(), args.clone()) .or_else(|err| match *err { ERR::ErrorFunctionNotFound(sig, ..) - if this_ptr.is_some() && sig.starts_with(self.fn_name()) => + if move_this_ptr_to_args.is_some() + && this_ptr.is_some() + && sig.starts_with(self.fn_name()) => { - let mut args = FnArgsVec::with_capacity(items.len() + 1); - args.push(this_ptr.as_mut().unwrap().clone()); - args.extend(IntoIterator::into_iter(items.clone())); - self.call_raw(ctx, this_ptr.as_deref_mut(), args) + let mut args2 = FnArgsVec::with_capacity(args.len() + 1); + let move_to_args = move_this_ptr_to_args.unwrap(); + if move_to_args == 0 { + args2.push(this_ptr.as_mut().unwrap().clone()); + args2.extend(args.clone()); + } else { + args2.extend(args.clone()); + args2.insert(move_to_args, this_ptr.as_mut().unwrap().clone()); + } + self.call_raw(ctx, None, args2) } _ => Err(err), }) .or_else(|err| match *err { ERR::ErrorFunctionNotFound(sig, ..) if sig.starts_with(self.fn_name()) => { - let mut args = FnArgsVec::with_capacity( - items.len() + extras.len() + if this_ptr.is_some() { 1 } else { 0 }, - ); - if let Some(ref mut this_ptr) = this_ptr { - args.push(this_ptr.clone()); + if let Some(move_to_args) = move_this_ptr_to_args { + if let Some(ref mut this_ptr) = this_ptr { + let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1); + if move_to_args == 0 { + args2.push(this_ptr.clone()); + args2.extend(args); + args2.extend(extras); + } else { + args2.extend(args); + args2.extend(extras); + args2.insert(move_to_args, this_ptr.clone()); + } + return self.call_raw(ctx, None, args2); + } } - args.extend(IntoIterator::into_iter(items)); - args.extend(IntoIterator::into_iter(extras)); - self.call_raw(ctx, this_ptr, args) + + let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len()); + args2.extend(args); + args2.extend(extras); + + self.call_raw(ctx, this_ptr, args2) } _ => Err(err), }) diff --git a/tests/arrays.rs b/tests/arrays.rs index f2343301..f10326dc 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -299,11 +299,19 @@ fn test_arrays_map_reduce() -> Result<(), Box> { assert_eq!(engine.eval::("[1].map(|x| x + 41)[0]")?, 42); assert_eq!(engine.eval::("[1].map(|| this + 41)[0]")?, 42); + assert_eq!( + engine.eval::("let x = [1, 2, 3]; x.for_each(|| this += 41); x[0]")?, + 42 + ); assert_eq!(engine.eval::("([1].map(|x| x + 41))[0]")?, 42); assert_eq!( engine.eval::("let c = 40; let y = 1; [1].map(|x, i| c + x + y + i)[0]")?, 42 ); + assert_eq!( + engine.eval::("let x = [1, 2, 3]; x.for_each(|i| this += i); x[2]")?, + 5 + ); assert_eq!( engine @@ -387,18 +395,31 @@ fn test_arrays_map_reduce() -> Result<(), Box> { 14 ); - assert_eq!( - engine.eval::( - " - let x = [1, 2, 3]; - x.reduce(|sum, v, i| { - if i == 0 { sum = 10 } - sum + v * v - }) - " - )?, - 24 - ); + // assert_eq!( + // engine.eval::( + // " + // let x = [1, 2, 3]; + // x.reduce(|sum, v, i| { + // if i == 0 { sum = 10 } + // sum + v * v + // }) + // " + // )?, + // 24 + // ); + + // assert_eq!( + // engine.eval::( + // " + // let x = [1, 2, 3]; + // x.reduce(|sum, i| { + // if i == 0 { sum = 10 } + // sum + this * this + // }) + // " + // )?, + // 24 + // ); assert_eq!( engine.eval::( From d47bfa431a4e7ba627aa6f0adb52d03b966a0ea8 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Dec 2022 11:46:11 +0800 Subject: [PATCH 2/7] Revise built-in macros. --- src/eval/stmt.rs | 4 +- src/func/builtin.rs | 354 ++++++++++++++++++++++---------------------- src/func/call.rs | 4 +- 3 files changed, 181 insertions(+), 181 deletions(-) diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 21fa73b3..c95c0321 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -140,14 +140,14 @@ impl Engine { let args = &mut [&mut *lock_guard, &mut new_val]; if self.fast_operators() { - if let Some((func, ctx)) = + if let Some((func, need_context)) = get_builtin_op_assignment_fn(op_assign_token.clone(), args[0], args[1]) { // Built-in found let op = op_assign_token.literal_syntax(); auto_restore! { let orig_level = global.level; global.level += 1 } - let context = if ctx { + let context = if need_context { Some((self, op, None, &*global, *op_pos).into()) } else { None diff --git a/src/func/builtin.rs b/src/func/builtin.rs index d27a39e7..e23f45ae 100644 --- a/src/func/builtin.rs +++ b/src/func/builtin.rs @@ -91,65 +91,65 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option { (|_, args| { + ($xx:ident $op:tt $yy:ident) => { Some((|_, args| { let x = &*args[0].read_lock::<$xx>().unwrap(); let y = &*args[1].read_lock::<$yy>().unwrap(); Ok((x $op y).into()) - }, false) }; - ($xx:ident . $func:ident ( $yy:ty )) => { (|_, args| { + }, false)) }; + ($xx:ident . $func:ident ( $yy:ty )) => { Some((|_, args| { let x = &*args[0].read_lock::<$xx>().unwrap(); let y = &*args[1].read_lock::<$yy>().unwrap(); Ok(x.$func(y).into()) - }, false) }; - ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { (|_, args| { + }, false)) }; + ($xx:ident . $func:ident ( $yy:ident . $yyy:ident () )) => { Some((|_, args| { let x = &*args[0].read_lock::<$xx>().unwrap(); let y = &*args[1].read_lock::<$yy>().unwrap(); Ok(x.$func(y.$yyy()).into()) - }, false) }; - ($func:ident ( $op:tt )) => { (|_, args| { + }, false)) }; + ($func:ident ( $op:tt )) => { Some((|_, args| { let (x, y) = $func(args); Ok((x $op y).into()) - }, false) }; - ($base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| { + }, false)) }; + ($base:ty => $xx:ident $op:tt $yy:ident) => { Some((|_, args| { let x = args[0].$xx().unwrap() as $base; let y = args[1].$yy().unwrap() as $base; Ok((x $op y).into()) - }, false) }; - ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { (|_, args| { + }, false)) }; + ($base:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty)) => { Some((|_, args| { let x = args[0].$xx().unwrap() as $base; let y = args[1].$yy().unwrap() as $base; Ok(x.$func(y as $yyy).into()) - }, false) }; - ($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| { + }, false)) }; + ($base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| { let x = args[0].$xx().unwrap() as $base; let y = args[1].$yy().unwrap() as $base; Ok($func(x, y).into()) - }, false) }; - ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { + }, false)) }; + ($base:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| { let x = args[0].$xx().unwrap() as $base; let y = args[1].$yy().unwrap() as $base; $func(x, y).map(Into::into) - }, false) }; - (from $base:ty => $xx:ident $op:tt $yy:ident) => { (|_, args| { + }, false)) }; + (from $base:ty => $xx:ident $op:tt $yy:ident) => { Some((|_, args| { let x = <$base>::from(args[0].$xx().unwrap()); let y = <$base>::from(args[1].$yy().unwrap()); Ok((x $op y).into()) - }, false) }; - (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| { + }, false)) }; + (from $base:ty => $xx:ident . $func:ident ( $yy:ident )) => { Some((|_, args| { let x = <$base>::from(args[0].$xx().unwrap()); let y = <$base>::from(args[1].$yy().unwrap()); Ok(x.$func(y).into()) - }, false) }; - (from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| { + }, false)) }; + (from $base:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| { let x = <$base>::from(args[0].$xx().unwrap()); let y = <$base>::from(args[1].$yy().unwrap()); Ok($func(x, y).into()) - }, false) }; - (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { + }, false)) }; + (from $base:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| { let x = <$base>::from(args[0].$xx().unwrap()); let y = <$base>::from(args[1].$yy().unwrap()); $func(x, y).map(Into::into) - }, false) }; + }, false)) }; } // Check for common patterns @@ -161,25 +161,25 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(INT => add(as_int, as_int))), - Minus => return Some(impl_op!(INT => subtract(as_int, as_int))), - Multiply => return Some(impl_op!(INT => multiply(as_int, as_int))), - Divide => return Some(impl_op!(INT => divide(as_int, as_int))), - Modulo => return Some(impl_op!(INT => modulo(as_int, as_int))), - PowerOf => return Some(impl_op!(INT => power(as_int, as_int))), - RightShift => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))), - LeftShift => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))), + Plus => return impl_op!(INT => add(as_int, as_int)), + Minus => return impl_op!(INT => subtract(as_int, as_int)), + Multiply => return impl_op!(INT => multiply(as_int, as_int)), + Divide => return impl_op!(INT => divide(as_int, as_int)), + Modulo => return impl_op!(INT => modulo(as_int, as_int)), + PowerOf => return impl_op!(INT => power(as_int, as_int)), + RightShift => return impl_op!(INT => Ok(shift_right(as_int, as_int))), + LeftShift => return impl_op!(INT => Ok(shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - Plus => return Some(impl_op!(INT => as_int + as_int)), - Minus => return Some(impl_op!(INT => as_int - as_int)), - Multiply => return Some(impl_op!(INT => as_int * as_int)), - Divide => return Some(impl_op!(INT => as_int / as_int)), - Modulo => return Some(impl_op!(INT => as_int % as_int)), - PowerOf => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + Plus => return impl_op!(INT => as_int + as_int), + Minus => return impl_op!(INT => as_int - as_int), + Multiply => return impl_op!(INT => as_int * as_int), + Divide => return impl_op!(INT => as_int / as_int), + Modulo => return impl_op!(INT => as_int % as_int), + PowerOf => return impl_op!(INT => as_int.pow(as_int as u32)), RightShift => { return Some(( |_, args| { @@ -204,32 +204,32 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(INT => as_int == as_int)), - NotEqualsTo => Some(impl_op!(INT => as_int != as_int)), - GreaterThan => Some(impl_op!(INT => as_int > as_int)), - GreaterThanEqualsTo => Some(impl_op!(INT => as_int >= as_int)), - LessThan => Some(impl_op!(INT => as_int < as_int)), - LessThanEqualsTo => Some(impl_op!(INT => as_int <= as_int)), - Ampersand => Some(impl_op!(INT => as_int & as_int)), - Pipe => Some(impl_op!(INT => as_int | as_int)), - XOr => Some(impl_op!(INT => as_int ^ as_int)), - ExclusiveRange => Some(impl_op!(INT => as_int .. as_int)), - InclusiveRange => Some(impl_op!(INT => as_int ..= as_int)), + EqualsTo => impl_op!(INT => as_int == as_int), + NotEqualsTo => impl_op!(INT => as_int != as_int), + GreaterThan => impl_op!(INT => as_int > as_int), + GreaterThanEqualsTo => impl_op!(INT => as_int >= as_int), + LessThan => impl_op!(INT => as_int < as_int), + LessThanEqualsTo => impl_op!(INT => as_int <= as_int), + Ampersand => impl_op!(INT => as_int & as_int), + Pipe => impl_op!(INT => as_int | as_int), + XOr => impl_op!(INT => as_int ^ as_int), + ExclusiveRange => impl_op!(INT => as_int .. as_int), + InclusiveRange => impl_op!(INT => as_int ..= as_int), _ => None, }; } if type1 == TypeId::of::() { return match op { - EqualsTo => Some(impl_op!(bool => as_bool == as_bool)), - NotEqualsTo => Some(impl_op!(bool => as_bool != as_bool)), - GreaterThan => Some(impl_op!(bool => as_bool > as_bool)), - GreaterThanEqualsTo => Some(impl_op!(bool => as_bool >= as_bool)), - LessThan => Some(impl_op!(bool => as_bool < as_bool)), - LessThanEqualsTo => Some(impl_op!(bool => as_bool <= as_bool)), - Ampersand => Some(impl_op!(bool => as_bool & as_bool)), - Pipe => Some(impl_op!(bool => as_bool | as_bool)), - XOr => Some(impl_op!(bool => as_bool ^ as_bool)), + EqualsTo => impl_op!(bool => as_bool == as_bool), + NotEqualsTo => impl_op!(bool => as_bool != as_bool), + GreaterThan => impl_op!(bool => as_bool > as_bool), + GreaterThanEqualsTo => impl_op!(bool => as_bool >= as_bool), + LessThan => impl_op!(bool => as_bool < as_bool), + LessThanEqualsTo => impl_op!(bool => as_bool <= as_bool), + Ampersand => impl_op!(bool => as_bool & as_bool), + Pipe => impl_op!(bool => as_bool | as_bool), + XOr => impl_op!(bool => as_bool ^ as_bool), _ => None, }; } @@ -250,13 +250,13 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(ImmutableString - ImmutableString)), - EqualsTo => Some(impl_op!(ImmutableString == ImmutableString)), - NotEqualsTo => Some(impl_op!(ImmutableString != ImmutableString)), - GreaterThan => Some(impl_op!(ImmutableString > ImmutableString)), - GreaterThanEqualsTo => Some(impl_op!(ImmutableString >= ImmutableString)), - LessThan => Some(impl_op!(ImmutableString < ImmutableString)), - LessThanEqualsTo => Some(impl_op!(ImmutableString <= ImmutableString)), + Minus => impl_op!(ImmutableString - ImmutableString), + EqualsTo => impl_op!(ImmutableString == ImmutableString), + NotEqualsTo => impl_op!(ImmutableString != ImmutableString), + GreaterThan => impl_op!(ImmutableString > ImmutableString), + GreaterThanEqualsTo => impl_op!(ImmutableString >= ImmutableString), + LessThan => impl_op!(ImmutableString < ImmutableString), + LessThanEqualsTo => impl_op!(ImmutableString <= ImmutableString), _ => None, }; } @@ -279,12 +279,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(char => as_char == as_char)), - NotEqualsTo => Some(impl_op!(char => as_char != as_char)), - GreaterThan => Some(impl_op!(char => as_char > as_char)), - GreaterThanEqualsTo => Some(impl_op!(char => as_char >= as_char)), - LessThan => Some(impl_op!(char => as_char < as_char)), - LessThanEqualsTo => Some(impl_op!(char => as_char <= as_char)), + EqualsTo => impl_op!(char => as_char == as_char), + NotEqualsTo => impl_op!(char => as_char != as_char), + GreaterThan => impl_op!(char => as_char > as_char), + GreaterThanEqualsTo => impl_op!(char => as_char >= as_char), + LessThan => impl_op!(char => as_char < as_char), + LessThanEqualsTo => impl_op!(char => as_char <= as_char), _ => None, }; } @@ -316,8 +316,8 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(Blob == Blob)), - NotEqualsTo => Some(impl_op!(Blob != Blob)), + EqualsTo => impl_op!(Blob == Blob), + NotEqualsTo => impl_op!(Blob != Blob), _ => None, }; } @@ -338,18 +338,18 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - Plus => Some(impl_op!(FLOAT => $xx + $yy)), - Minus => Some(impl_op!(FLOAT => $xx - $yy)), - Multiply => Some(impl_op!(FLOAT => $xx * $yy)), - Divide => Some(impl_op!(FLOAT => $xx / $yy)), - Modulo => Some(impl_op!(FLOAT => $xx % $yy)), - PowerOf => Some(impl_op!(FLOAT => $xx.powf($yy as FLOAT))), - EqualsTo => Some(impl_op!(FLOAT => $xx == $yy)), - NotEqualsTo => Some(impl_op!(FLOAT => $xx != $yy)), - GreaterThan => Some(impl_op!(FLOAT => $xx > $yy)), - GreaterThanEqualsTo => Some(impl_op!(FLOAT => $xx >= $yy)), - LessThan => Some(impl_op!(FLOAT => $xx < $yy)), - LessThanEqualsTo => Some(impl_op!(FLOAT => $xx <= $yy)), + Plus => impl_op!(FLOAT => $xx + $yy), + Minus => impl_op!(FLOAT => $xx - $yy), + Multiply => impl_op!(FLOAT => $xx * $yy), + Divide => impl_op!(FLOAT => $xx / $yy), + Modulo => impl_op!(FLOAT => $xx % $yy), + PowerOf => impl_op!(FLOAT => $xx.powf($yy as FLOAT)), + EqualsTo => impl_op!(FLOAT => $xx == $yy), + NotEqualsTo => impl_op!(FLOAT => $xx != $yy), + GreaterThan => impl_op!(FLOAT => $xx > $yy), + GreaterThanEqualsTo => impl_op!(FLOAT => $xx >= $yy), + LessThan => impl_op!(FLOAT => $xx < $yy), + LessThanEqualsTo => impl_op!(FLOAT => $xx <= $yy), _ => None, }; } @@ -373,12 +373,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(from Decimal => add($xx, $yy))), - Minus => return Some(impl_op!(from Decimal => subtract($xx, $yy))), - Multiply => return Some(impl_op!(from Decimal => multiply($xx, $yy))), - Divide => return Some(impl_op!(from Decimal => divide($xx, $yy))), - Modulo => return Some(impl_op!(from Decimal => modulo($xx, $yy))), - PowerOf => return Some(impl_op!(from Decimal => power($xx, $yy))), + Plus => return impl_op!(from Decimal => add($xx, $yy)), + Minus => return impl_op!(from Decimal => subtract($xx, $yy)), + Multiply => return impl_op!(from Decimal => multiply($xx, $yy)), + Divide => return impl_op!(from Decimal => divide($xx, $yy)), + Modulo => return impl_op!(from Decimal => modulo($xx, $yy)), + PowerOf => return impl_op!(from Decimal => power($xx, $yy)), _ => () } @@ -387,22 +387,22 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option return Some(impl_op!(from Decimal => $xx + $yy)), - Minus => return Some(impl_op!(from Decimal => $xx - $yy)), - Multiply => return Some(impl_op!(from Decimal => $xx * $yy)), - Divide => return Some(impl_op!(from Decimal => $xx / $yy)), - Modulo => return Some(impl_op!(from Decimal => $xx % $yy)), - PowerOf => return Some(impl_op!(from Decimal => $xx.powd($yy))), + Plus => return impl_op!(from Decimal => $xx + $yy), + Minus => return impl_op!(from Decimal => $xx - $yy), + Multiply => return impl_op!(from Decimal => $xx * $yy), + Divide => return impl_op!(from Decimal => $xx / $yy), + Modulo => return impl_op!(from Decimal => $xx % $yy), + PowerOf => return impl_op!(from Decimal => $xx.powd($yy)), _ => () } return match op { - EqualsTo => Some(impl_op!(from Decimal => $xx == $yy)), - NotEqualsTo => Some(impl_op!(from Decimal => $xx != $yy)), - GreaterThan => Some(impl_op!(from Decimal => $xx > $yy)), - GreaterThanEqualsTo => Some(impl_op!(from Decimal => $xx >= $yy)), - LessThan => Some(impl_op!(from Decimal => $xx < $yy)), - LessThanEqualsTo => Some(impl_op!(from Decimal => $xx <= $yy)), + EqualsTo => impl_op!(from Decimal => $xx == $yy), + NotEqualsTo => impl_op!(from Decimal => $xx != $yy), + GreaterThan => impl_op!(from Decimal => $xx > $yy), + GreaterThanEqualsTo => impl_op!(from Decimal => $xx >= $yy), + LessThan => impl_op!(from Decimal => $xx < $yy), + LessThanEqualsTo => impl_op!(from Decimal => $xx <= $yy), _ => None }; } @@ -444,12 +444,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(get_s1s2(==))), - NotEqualsTo => Some(impl_op!(get_s1s2(!=))), - GreaterThan => Some(impl_op!(get_s1s2(>))), - GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), - LessThan => Some(impl_op!(get_s1s2(<))), - LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), + EqualsTo => impl_op!(get_s1s2(==)), + NotEqualsTo => impl_op!(get_s1s2(!=)), + GreaterThan => impl_op!(get_s1s2(>)), + GreaterThanEqualsTo => impl_op!(get_s1s2(>=)), + LessThan => impl_op!(get_s1s2(<)), + LessThanEqualsTo => impl_op!(get_s1s2(<=)), _ => None, }; } @@ -486,12 +486,12 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option Some(impl_op!(get_s1s2(==))), - NotEqualsTo => Some(impl_op!(get_s1s2(!=))), - GreaterThan => Some(impl_op!(get_s1s2(>))), - GreaterThanEqualsTo => Some(impl_op!(get_s1s2(>=))), - LessThan => Some(impl_op!(get_s1s2(<))), - LessThanEqualsTo => Some(impl_op!(get_s1s2(<=))), + EqualsTo => impl_op!(get_s1s2(==)), + NotEqualsTo => impl_op!(get_s1s2(!=)), + GreaterThan => impl_op!(get_s1s2(>)), + GreaterThanEqualsTo => impl_op!(get_s1s2(>=)), + LessThan => impl_op!(get_s1s2(<)), + LessThanEqualsTo => impl_op!(get_s1s2(<=)), _ => None, }; } @@ -568,16 +568,16 @@ pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option() && type1 == type2 { return match op { - EqualsTo => Some(impl_op!(ExclusiveRange == ExclusiveRange)), - NotEqualsTo => Some(impl_op!(ExclusiveRange != ExclusiveRange)), + EqualsTo => impl_op!(ExclusiveRange == ExclusiveRange), + NotEqualsTo => impl_op!(ExclusiveRange != ExclusiveRange), _ => None, }; } if type1 == TypeId::of::() && type1 == type2 { return match op { - EqualsTo => Some(impl_op!(InclusiveRange == InclusiveRange)), - NotEqualsTo => Some(impl_op!(InclusiveRange != InclusiveRange)), + EqualsTo => impl_op!(InclusiveRange == InclusiveRange), + NotEqualsTo => impl_op!(InclusiveRange != InclusiveRange), _ => None, }; } @@ -626,54 +626,54 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti let type2 = y.type_id(); macro_rules! impl_op { - ($x:ty = x $op:tt $yy:ident) => { (|_, args| { + ($x:ty = x $op:tt $yy:ident) => { Some((|_, args| { let x = args[0].$yy().unwrap(); let y = args[1].$yy().unwrap() as $x; Ok((*args[0].write_lock::<$x>().unwrap() = x $op y).into()) - }, false) }; - ($x:ident $op:tt $yy:ident) => { (|_, args| { + }, false)) }; + ($x:ident $op:tt $yy:ident) => { Some((|_, args| { let y = args[1].$yy().unwrap() as $x; Ok((*args[0].write_lock::<$x>().unwrap() $op y).into()) - }, false) }; - ($x:ident $op:tt $yy:ident as $yyy:ty) => { (|_, args| { + }, false)) }; + ($x:ident $op:tt $yy:ident as $yyy:ty) => { Some((|_, args| { let y = args[1].$yy().unwrap() as $yyy; Ok((*args[0].write_lock::<$x>().unwrap() $op y).into()) - }, false) }; - ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { (|_, args| { + }, false)) }; + ($x:ty => $xx:ident . $func:ident ( $yy:ident as $yyy:ty )) => { Some((|_, args| { let x = args[0].$xx().unwrap(); let y = args[1].$yy().unwrap() as $x; Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y as $yyy)).into()) - }, false) }; - ($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| { + }, false)) }; + ($x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| { let x = args[0].$xx().unwrap(); let y = args[1].$yy().unwrap() as $x; let v: Dynamic = $func(x, y).into(); Ok((*args[0].write_lock().unwrap() = v).into()) - }, false) }; - ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { + }, false)) }; + ($x:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| { let x = args[0].$xx().unwrap(); let y = args[1].$yy().unwrap() as $x; Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into()) - }, false) }; - (from $x:ident $op:tt $yy:ident) => { (|_, args| { + }, false)) }; + (from $x:ident $op:tt $yy:ident) => { Some((|_, args| { let y = <$x>::from(args[1].$yy().unwrap()); Ok((*args[0].write_lock::<$x>().unwrap() $op y).into()) - }, false) }; - (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { (|_, args| { + }, false)) }; + (from $x:ty => $xx:ident . $func:ident ( $yy:ident )) => { Some((|_, args| { let x = args[0].$xx().unwrap(); let y = <$x>::from(args[1].$yy().unwrap()); Ok((*args[0].write_lock::<$x>().unwrap() = x.$func(y)).into()) - }, false) }; - (from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { (|_, args| { + }, false)) }; + (from $x:ty => Ok($func:ident ( $xx:ident, $yy:ident ))) => { Some((|_, args| { let x = args[0].$xx().unwrap(); let y = <$x>::from(args[1].$yy().unwrap()); Ok((*args[0].write_lock().unwrap() = $func(x, y).into()).into()) - }, false) }; - (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { (|_, args| { + }, false)) }; + (from $x:ty => $func:ident ( $xx:ident, $yy:ident )) => { Some((|_, args| { let x = args[0].$xx().unwrap(); let y = <$x>::from(args[1].$yy().unwrap()); Ok((*args[0].write_lock().unwrap() = $func(x, y)?).into()) - }, false) }; + }, false)) }; } // Check for common patterns @@ -685,25 +685,25 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti #[cfg(not(feature = "unchecked"))] match op { - PlusAssign => return Some(impl_op!(INT => add(as_int, as_int))), - MinusAssign => return Some(impl_op!(INT => subtract(as_int, as_int))), - MultiplyAssign => return Some(impl_op!(INT => multiply(as_int, as_int))), - DivideAssign => return Some(impl_op!(INT => divide(as_int, as_int))), - ModuloAssign => return Some(impl_op!(INT => modulo(as_int, as_int))), - PowerOfAssign => return Some(impl_op!(INT => power(as_int, as_int))), - RightShiftAssign => return Some(impl_op!(INT => Ok(shift_right(as_int, as_int)))), - LeftShiftAssign => return Some(impl_op!(INT => Ok(shift_left(as_int, as_int)))), + PlusAssign => return impl_op!(INT => add(as_int, as_int)), + MinusAssign => return impl_op!(INT => subtract(as_int, as_int)), + MultiplyAssign => return impl_op!(INT => multiply(as_int, as_int)), + DivideAssign => return impl_op!(INT => divide(as_int, as_int)), + ModuloAssign => return impl_op!(INT => modulo(as_int, as_int)), + PowerOfAssign => return impl_op!(INT => power(as_int, as_int)), + RightShiftAssign => return impl_op!(INT => Ok(shift_right(as_int, as_int))), + LeftShiftAssign => return impl_op!(INT => Ok(shift_left(as_int, as_int))), _ => (), } #[cfg(feature = "unchecked")] match op { - PlusAssign => return Some(impl_op!(INT += as_int)), - MinusAssign => return Some(impl_op!(INT -= as_int)), - MultiplyAssign => return Some(impl_op!(INT *= as_int)), - DivideAssign => return Some(impl_op!(INT /= as_int)), - ModuloAssign => return Some(impl_op!(INT %= as_int)), - PowerOfAssign => return Some(impl_op!(INT => as_int.pow(as_int as u32))), + PlusAssign => return impl_op!(INT += as_int), + MinusAssign => return impl_op!(INT -= as_int), + MultiplyAssign => return impl_op!(INT *= as_int), + DivideAssign => return impl_op!(INT /= as_int), + ModuloAssign => return impl_op!(INT %= as_int), + PowerOfAssign => return impl_op!(INT => as_int.pow(as_int as u32)), RightShiftAssign => { return Some(( |_, args| { @@ -730,17 +730,17 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti } return match op { - AndAssign => Some(impl_op!(INT &= as_int)), - OrAssign => Some(impl_op!(INT |= as_int)), - XOrAssign => Some(impl_op!(INT ^= as_int)), + AndAssign => impl_op!(INT &= as_int), + OrAssign => impl_op!(INT |= as_int), + XOrAssign => impl_op!(INT ^= as_int), _ => None, }; } if type1 == TypeId::of::() { return match op { - AndAssign => Some(impl_op!(bool = x && as_bool)), - OrAssign => Some(impl_op!(bool = x || as_bool)), + AndAssign => impl_op!(bool = x && as_bool), + OrAssign => impl_op!(bool = x || as_bool), _ => None, }; } @@ -861,12 +861,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti ($x:ident, $xx:ident, $y:ty, $yy:ident) => { if (type1, type2) == (TypeId::of::<$x>(), TypeId::of::<$y>()) { return match op { - PlusAssign => Some(impl_op!($x += $yy)), - MinusAssign => Some(impl_op!($x -= $yy)), - MultiplyAssign => Some(impl_op!($x *= $yy)), - DivideAssign => Some(impl_op!($x /= $yy)), - ModuloAssign => Some(impl_op!($x %= $yy)), - PowerOfAssign => Some(impl_op!($x => $xx.powf($yy as $x))), + PlusAssign => impl_op!($x += $yy), + MinusAssign => impl_op!($x -= $yy), + MultiplyAssign => impl_op!($x *= $yy), + DivideAssign => impl_op!($x /= $yy), + ModuloAssign => impl_op!($x %= $yy), + PowerOfAssign => impl_op!($x => $xx.powf($yy as $x)), _ => None, }; } @@ -889,12 +889,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti #[cfg(not(feature = "unchecked"))] return match op { - PlusAssign => Some(impl_op!(from $x => add($xx, $yy))), - MinusAssign => Some(impl_op!(from $x => subtract($xx, $yy))), - MultiplyAssign => Some(impl_op!(from $x => multiply($xx, $yy))), - DivideAssign => Some(impl_op!(from $x => divide($xx, $yy))), - ModuloAssign => Some(impl_op!(from $x => modulo($xx, $yy))), - PowerOfAssign => Some(impl_op!(from $x => power($xx, $yy))), + PlusAssign => impl_op!(from $x => add($xx, $yy)), + MinusAssign => impl_op!(from $x => subtract($xx, $yy)), + MultiplyAssign => impl_op!(from $x => multiply($xx, $yy)), + DivideAssign => impl_op!(from $x => divide($xx, $yy)), + ModuloAssign => impl_op!(from $x => modulo($xx, $yy)), + PowerOfAssign => impl_op!(from $x => power($xx, $yy)), _ => None, }; @@ -903,12 +903,12 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti #[cfg(feature = "unchecked")] return match op { - PlusAssign => Some(impl_op!(from $x += $yy)), - MinusAssign => Some(impl_op!(from $x -= $yy)), - MultiplyAssign => Some(impl_op!(from $x *= $yy)), - DivideAssign => Some(impl_op!(from $x /= $yy)), - ModuloAssign => Some(impl_op!(from $x %= $yy)), - PowerOfAssign => Some(impl_op!(from $x => $xx.powd($yy))), + PlusAssign => impl_op!(from $x += $yy), + MinusAssign => impl_op!(from $x -= $yy), + MultiplyAssign => impl_op!(from $x *= $yy), + DivideAssign => impl_op!(from $x /= $yy), + ModuloAssign => impl_op!(from $x %= $yy), + PowerOfAssign => impl_op!(from $x => $xx.powd($yy)), _ => None, }; } @@ -939,7 +939,7 @@ pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Opti }, CHECKED_BUILD, )), - MinusAssign => Some(impl_op!(ImmutableString -= as_char as char)), + MinusAssign => impl_op!(ImmutableString -= as_char as char), _ => None, }; } diff --git a/src/func/call.rs b/src/func/call.rs index 42452d47..197e4d27 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -1611,13 +1611,13 @@ impl Engine { let operands = &mut [&mut lhs, &mut rhs]; - if let Some((func, ctx)) = + if let Some((func, need_context)) = get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1]) { // Built-in found auto_restore! { let orig_level = global.level; global.level += 1 } - let context = if ctx { + let context = if need_context { Some((self, name.as_str(), None, &*global, pos).into()) } else { None From c5cb2d5e0f080ef72192f8a8145e58d1821af10e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Dec 2022 17:32:46 +0800 Subject: [PATCH 3/7] Simplify Array::pad. --- src/eval/data_check.rs | 92 +++++++++++++++++++++++-------------- src/packages/array_basic.rs | 49 +++----------------- 2 files changed, 63 insertions(+), 78 deletions(-) diff --git a/src/eval/data_check.rs b/src/eval/data_check.rs index 1e9e4a7f..2b031bcd 100644 --- a/src/eval/data_check.rs +++ b/src/eval/data_check.rs @@ -9,6 +9,60 @@ use std::borrow::Borrow; use std::prelude::v1::*; impl Dynamic { + /// Recursively calculate the sizes of an array. + /// + /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. + /// + /// # Panics + /// + /// Panics if any interior data is shared (should never happen). + #[cfg(not(feature = "no_index"))] + #[inline] + pub(crate) fn calc_array_sizes(array: &crate::Array, _top: bool) -> (usize, usize, usize) { + array + .iter() + .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { + Union::Array(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a + 1, mx + m, sx + s) + } + Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), + #[cfg(not(feature = "no_object"))] + Union::Map(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a + 1, mx + m, sx + s) + } + Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), + _ => (ax + 1, mx, sx), + }) + } + /// Recursively calculate the sizes of a map. + /// + /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. + /// + /// # Panics + /// + /// Panics if any interior data is shared (should never happen). + #[cfg(not(feature = "no_object"))] + #[inline] + pub(crate) fn calc_map_sizes(map: &crate::Map, _top: bool) -> (usize, usize, usize) { + map.values() + .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { + #[cfg(not(feature = "no_index"))] + Union::Array(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a, mx + m + 1, sx + s) + } + #[cfg(not(feature = "no_index"))] + Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), + Union::Map(..) => { + let (a, m, s) = value.calc_data_sizes(false); + (ax + a, mx + m + 1, sx + s) + } + Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), + _ => (ax, mx + 1, sx), + }) + } /// Recursively calculate the sizes of a value. /// /// Sizes returned are `(` [`Array`][crate::Array], [`Map`][crate::Map] and [`String`] `)`. @@ -16,47 +70,15 @@ impl Dynamic { /// # Panics /// /// Panics if any interior data is shared (should never happen). + #[inline] pub(crate) fn calc_data_sizes(&self, _top: bool) -> (usize, usize, usize) { match self.0 { #[cfg(not(feature = "no_index"))] - Union::Array(ref arr, ..) => { - arr.iter() - .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { - Union::Array(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a + 1, mx + m, sx + s) - } - Union::Blob(ref a, ..) => (ax + 1 + a.len(), mx, sx), - #[cfg(not(feature = "no_object"))] - Union::Map(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a + 1, mx + m, sx + s) - } - Union::Str(ref s, ..) => (ax + 1, mx, sx + s.len()), - _ => (ax + 1, mx, sx), - }) - } + Union::Array(ref arr, ..) => Self::calc_array_sizes(&**arr, _top), #[cfg(not(feature = "no_index"))] Union::Blob(ref blob, ..) => (blob.len(), 0, 0), #[cfg(not(feature = "no_object"))] - Union::Map(ref map, ..) => { - map.values() - .fold((0, 0, 0), |(ax, mx, sx), value| match value.0 { - #[cfg(not(feature = "no_index"))] - Union::Array(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a, mx + m + 1, sx + s) - } - #[cfg(not(feature = "no_index"))] - Union::Blob(ref a, ..) => (ax + a.len(), mx, sx), - Union::Map(..) => { - let (a, m, s) = value.calc_data_sizes(false); - (ax + a, mx + m + 1, sx + s) - } - Union::Str(ref s, ..) => (ax, mx + 1, sx + s.len()), - _ => (ax, mx + 1, sx), - }) - } + Union::Map(ref map, ..) => Self::calc_map_sizes(&**map, _top), Union::Str(ref s, ..) => (0, 0, s.len()), #[cfg(not(feature = "no_closure"))] Union::Shared(..) if _top => self.read_lock::().unwrap().calc_data_sizes(true), diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index c1502d4a..b8396991 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -235,52 +235,15 @@ pub mod array_functions { // Check if array will be over max size limit #[cfg(not(feature = "unchecked"))] - { - use crate::types::dynamic::Union; + if _ctx.engine().max_array_size() > 0 { + let pad = len - array.len(); + let (a, m, s) = Dynamic::calc_array_sizes(array, true); + let (ax, mx, sx) = item.calc_data_sizes(true); - if _ctx.engine().max_array_size() > 0 && len > _ctx.engine().max_array_size() { - return Err( - ERR::ErrorDataTooLarge("Size of array".to_string(), Position::NONE).into(), - ); - } - - let check_sizes = match item.0 { - Union::Str(..) => true, - Union::Array(..) => true, - #[cfg(not(feature = "no_object"))] - Union::Map(..) => true, - _ => false, - }; - - if check_sizes { - let mut arr_len = array.len(); - let mut arr = Dynamic::from_array(mem::take(array)); - - let (mut a1, mut m1, mut s1) = arr.calc_data_sizes(true); - let (a2, m2, s2) = item.calc_data_sizes(true); - - { - let mut guard = arr.write_lock::().unwrap(); - - while arr_len < len { - a1 += a2; - m1 += m2; - s1 += s2; - - _ctx.engine().throw_on_size((a1, m1, s1))?; - - guard.push(item.clone()); - arr_len += 1; - } - } - - *array = arr.into_array().unwrap(); - } else { - array.resize(len, item); - } + _ctx.engine() + .throw_on_size((a + pad + ax * pad, m + mx * pad, s + sx * pad))?; } - #[cfg(feature = "unchecked")] array.resize(len, item); Ok(()) From 07f522e6d7c26010865aa1879d14574907ed896b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 26 Dec 2022 22:05:29 +0800 Subject: [PATCH 4/7] Fix array bug. --- src/types/fn_ptr.rs | 12 ++++++------ tests/arrays.rs | 12 ++++++++++++ 2 files changed, 18 insertions(+), 6 deletions(-) diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index ca678840..34dcc483 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -421,9 +421,12 @@ impl FnPtr { ) -> RhaiResult { #[cfg(not(feature = "no_function"))] if let Some(arity) = self.fn_def().map(|f| f.params.len()) { + if arity == N + self.curry().len() { + return self.call_raw(ctx, this_ptr, args); + } if let Some(move_to_args) = move_this_ptr_to_args { if this_ptr.is_some() { - if arity == N + 1 { + if arity == N + 1 + self.curry().len() { let mut args2 = FnArgsVec::with_capacity(args.len() + 1); if move_to_args == 0 { args2.push(this_ptr.as_mut().unwrap().clone()); @@ -434,7 +437,7 @@ impl FnPtr { } return self.call_raw(ctx, None, args2); } - if arity == N + E + 1 { + if arity == N + E + 1 + self.curry().len() { let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len() + 1); if move_to_args == 0 { args2.push(this_ptr.as_mut().unwrap().clone()); @@ -449,10 +452,7 @@ impl FnPtr { } } } - if arity == N { - return self.call_raw(ctx, this_ptr, args); - } - if arity == N + E { + if arity == N + E + self.curry().len() { let mut args2 = FnArgsVec::with_capacity(args.len() + extras.len()); args2.extend(args); args2.extend(extras); diff --git a/tests/arrays.rs b/tests/arrays.rs index f10326dc..e0d5226e 100644 --- a/tests/arrays.rs +++ b/tests/arrays.rs @@ -303,6 +303,18 @@ fn test_arrays_map_reduce() -> Result<(), Box> { engine.eval::("let x = [1, 2, 3]; x.for_each(|| this += 41); x[0]")?, 42 ); + assert_eq!( + engine.eval::( + " + let x = [1, 2, 3]; + let sum = 0; + let factor = 2; + x.for_each(|| sum += this * factor); + sum + " + )?, + 12 + ); assert_eq!(engine.eval::("([1].map(|x| x + 41))[0]")?, 42); assert_eq!( engine.eval::("let c = 40; let y = 1; [1].map(|x, i| c + x + y + i)[0]")?, From 7c00b749169e82dd1e3a8190058219a675458768 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Dec 2022 10:09:40 +0800 Subject: [PATCH 5/7] Allow string interpolation to work with no packages. --- CHANGELOG.md | 1 + src/engine.rs | 5 -- src/eval/expr.rs | 107 ++++++++++++++++++----------------- src/eval/stmt.rs | 3 +- src/optimizer.rs | 2 +- src/packages/string_basic.rs | 45 ++++++++++----- tests/data_size.rs | 2 +- tests/string.rs | 3 +- 8 files changed, 93 insertions(+), 75 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a81820de..1b66ac38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Bug fixes * Integer numbers that are too large to deserialize into `INT` now fall back to `Decimal` or `FLOAT` instead of silently truncating. * Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit. * Closures containing a single expression are now allowed in `Engine::eval_expression` etc. +* Strings interpolation now works under `Engine::new_raw` without any standard package. Breaking API changes -------------------- diff --git a/src/engine.rs b/src/engine.rs index 2e239912..3caa8183 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -52,11 +52,6 @@ pub const FN_ANONYMOUS: &str = "anon$"; /// function to compare two [`Dynamic`] values. pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax(); -/// Standard concatenation operator. -/// -/// Used primarily to build up interpolated strings. -pub const OP_CONCAT: &str = Token::PlusAssign.literal_syntax(); - /// Standard containment testing function. /// /// The `in` operator is implemented as a call to this function. diff --git a/src/eval/expr.rs b/src/eval/expr.rs index a87d91e0..d04aec44 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -1,13 +1,14 @@ //! Module defining functions for evaluating an expression. use super::{Caches, EvalContext, GlobalRuntimeState, Target}; -use crate::ast::{Expr, OpAssignment}; -use crate::engine::{KEYWORD_THIS, OP_CONCAT}; +use crate::ast::Expr; +use crate::engine::KEYWORD_THIS; +use crate::packages::string_basic::{print_with_func, FUNC_TO_STRING}; use crate::types::dynamic::AccessMode; -use crate::{Dynamic, Engine, Position, RhaiResult, RhaiResultOf, Scope, ERR}; -use std::num::NonZeroUsize; +use crate::{Dynamic, Engine, RhaiResult, RhaiResultOf, Scope, SmartString, ERR}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +use std::{fmt::Write, num::NonZeroUsize}; impl Engine { /// Search for a module within an imports stack. @@ -283,72 +284,75 @@ impl Engine { // `... ${...} ...` Expr::InterpolatedString(x, _) => { - let mut concat = self.const_empty_string().into(); - let target = &mut concat; + let mut concat = SmartString::new_const(); - let mut op_info = OpAssignment::new_op_assignment(OP_CONCAT, Position::NONE); + x.iter().try_for_each(|expr| -> RhaiResultOf<()> { + let item = &mut self + .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)? + .flatten(); + let pos = expr.position(); - x.iter() - .try_for_each(|expr| { - let item = self - .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), expr)? - .flatten(); + if item.is_string() { + write!(concat, "{item}").unwrap(); + } else { + let source = global.source(); + let context = &(self, FUNC_TO_STRING, source, &*global, pos).into(); + let display = print_with_func(FUNC_TO_STRING, context, item); + write!(concat, "{}", display).unwrap(); + } - op_info.pos = expr.start_position(); + #[cfg(not(feature = "unchecked"))] + self.throw_on_size((0, 0, concat.len())) + .map_err(|err| err.fill_position(pos))?; - self.eval_op_assignment(global, caches, &op_info, expr, target, item) - }) - .map(|_| concat.take_or_clone()) - .and_then(|r| self.check_data_size(r, expr.start_position())) + Ok(()) + })?; + + Ok(self.get_interned_string(concat).into()) } #[cfg(not(feature = "no_index"))] Expr::Array(x, ..) => { + let mut array = crate::Array::with_capacity(x.len()); + #[cfg(not(feature = "unchecked"))] let mut total_data_sizes = (0, 0, 0); - x.iter() - .try_fold( - crate::Array::with_capacity(x.len()), - |mut array, item_expr| { - let value = self - .eval_expr( - global, - caches, - scope, - this_ptr.as_deref_mut(), - item_expr, - )? - .flatten(); + x.iter().try_for_each(|item_expr| -> RhaiResultOf<()> { + let value = self + .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), item_expr)? + .flatten(); - #[cfg(not(feature = "unchecked"))] - if self.has_data_size_limit() { - let val_sizes = value.calc_data_sizes(true); + #[cfg(not(feature = "unchecked"))] + if self.has_data_size_limit() { + let val_sizes = value.calc_data_sizes(true); - total_data_sizes = ( - total_data_sizes.0 + val_sizes.0, - total_data_sizes.1 + val_sizes.1, - total_data_sizes.2 + val_sizes.2, - ); - self.throw_on_size(total_data_sizes) - .map_err(|err| err.fill_position(item_expr.position()))?; - } + total_data_sizes = ( + total_data_sizes.0 + val_sizes.0 + 1, + total_data_sizes.1 + val_sizes.1, + total_data_sizes.2 + val_sizes.2, + ); + self.throw_on_size(total_data_sizes) + .map_err(|err| err.fill_position(item_expr.position()))?; + } - array.push(value); + array.push(value); - Ok(array) - }, - ) - .map(Into::into) + Ok(()) + })?; + + Ok(Dynamic::from_array(array)) } #[cfg(not(feature = "no_object"))] Expr::Map(x, ..) => { + let mut map = x.1.clone(); + #[cfg(not(feature = "unchecked"))] let mut total_data_sizes = (0, 0, 0); x.0.iter() - .try_fold(x.1.clone(), |mut map, (key, value_expr)| { + .try_for_each(|(key, value_expr)| -> RhaiResultOf<()> { let value = self .eval_expr(global, caches, scope, this_ptr.as_deref_mut(), value_expr)? .flatten(); @@ -358,7 +362,7 @@ impl Engine { let delta = value.calc_data_sizes(true); total_data_sizes = ( total_data_sizes.0 + delta.0, - total_data_sizes.1 + delta.1, + total_data_sizes.1 + delta.1 + 1, total_data_sizes.2 + delta.2, ); self.throw_on_size(total_data_sizes) @@ -367,9 +371,10 @@ impl Engine { *map.get_mut(key.as_str()).unwrap() = value; - Ok(map) - }) - .map(Into::into) + Ok(()) + })?; + + Ok(Dynamic::from_map(map)) } Expr::And(x, ..) => Ok((self diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index c95c0321..49627c08 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -148,7 +148,8 @@ impl Engine { auto_restore! { let orig_level = global.level; global.level += 1 } let context = if need_context { - Some((self, op, None, &*global, *op_pos).into()) + let source = global.source(); + Some((self, op, source, &*global, *op_pos).into()) } else { None }; diff --git a/src/optimizer.rs b/src/optimizer.rs index 89d66e47..0b8ff895 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1141,7 +1141,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1]) .and_then(|(f, ctx)| { let context = if ctx { - Some((state.engine, x.name.as_str(),None, &state.global, *pos).into()) + Some((state.engine, x.name.as_str(), None, &state.global, *pos).into()) } else { None }; diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index 686951f7..f2ff7304 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -44,7 +44,14 @@ pub fn print_with_func( result.into_immutable_string().expect("`ImmutableString`") } Ok(result) => ctx.engine().map_type_name(result.type_name()).into(), - Err(_) => ctx.engine().map_type_name(value.type_name()).into(), + Err(_) => { + let mut buf = SmartString::new_const(); + match fn_name { + FUNC_TO_DEBUG => write!(&mut buf, "{value:?}").unwrap(), + _ => write!(&mut buf, "{value}").unwrap(), + } + ctx.engine().map_type_name(&buf).into() + } } } @@ -58,7 +65,9 @@ mod print_debug_functions { /// Convert the value of the `item` into a string. #[rhai_fn(name = "to_string", pure)] pub fn to_string_generic(ctx: NativeCallContext, item: &mut Dynamic) -> ImmutableString { - ctx.engine().map_type_name(&item.to_string()).into() + let mut buf = SmartString::new_const(); + write!(&mut buf, "{item}").unwrap(); + ctx.engine().map_type_name(&buf).into() } /// Convert the value of the `item` into a string in debug format. #[rhai_fn(name = "debug", pure)] @@ -95,7 +104,9 @@ mod print_debug_functions { /// Return the character into a string. #[rhai_fn(name = "print", name = "to_string")] pub fn print_char(character: char) -> ImmutableString { - character.to_string().into() + let mut buf = SmartString::new_const(); + buf.push(character); + buf.into() } /// Convert the string into debug format. #[rhai_fn(name = "debug", name = "to_debug")] @@ -108,13 +119,17 @@ mod print_debug_functions { /// Convert the function pointer into a string in debug format. #[rhai_fn(name = "debug", name = "to_debug", pure)] pub fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString { - f.to_string().into() + let mut buf = SmartString::new_const(); + write!(&mut buf, "{f}").unwrap(); + buf.into() } /// Return the boolean value into a string. #[rhai_fn(name = "print", name = "to_string")] pub fn print_bool(value: bool) -> ImmutableString { - value.to_string().into() + let mut buf = SmartString::new_const(); + write!(&mut buf, "{value}").unwrap(); + buf.into() } /// Convert the boolean value into a string in debug format. #[rhai_fn(name = "debug", name = "to_debug")] @@ -141,30 +156,32 @@ mod print_debug_functions { #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "print", name = "to_string")] pub fn print_f64(number: f64) -> ImmutableString { - crate::types::FloatWrapper::new(number).to_string().into() + let mut buf = SmartString::new_const(); + write!(&mut buf, "{}", crate::types::FloatWrapper::new(number)).unwrap(); + buf.into() } /// Convert the value of `number` into a string. #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "print", name = "to_string")] pub fn print_f32(number: f32) -> ImmutableString { - crate::types::FloatWrapper::new(number).to_string().into() + let mut buf = SmartString::new_const(); + write!(&mut buf, "{}", crate::types::FloatWrapper::new(number)).unwrap(); + buf.into() } /// Convert the value of `number` into a string. #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "debug", name = "to_debug")] pub fn debug_f64(number: f64) -> ImmutableString { - let number = crate::types::FloatWrapper::new(number); let mut buf = SmartString::new_const(); - write!(&mut buf, "{number:?}").unwrap(); + write!(&mut buf, "{:?}", crate::types::FloatWrapper::new(number)).unwrap(); buf.into() } /// Convert the value of `number` into a string. #[cfg(not(feature = "no_float"))] #[rhai_fn(name = "debug", name = "to_debug")] pub fn debug_f32(number: f32) -> ImmutableString { - let number = crate::types::FloatWrapper::new(number); let mut buf = SmartString::new_const(); - write!(&mut buf, "{number:?}").unwrap(); + write!(&mut buf, "{:?}", crate::types::FloatWrapper::new(number)).unwrap(); buf.into() } @@ -179,7 +196,7 @@ mod print_debug_functions { )] pub fn format_array(ctx: NativeCallContext, array: &mut Array) -> ImmutableString { let len = array.len(); - let mut result = String::with_capacity(len * 5 + 2); + let mut result = SmartString::new_const(); result.push('['); array.iter_mut().enumerate().for_each(|(i, x)| { @@ -204,12 +221,10 @@ mod print_debug_functions { )] pub fn format_map(ctx: NativeCallContext, map: &mut Map) -> ImmutableString { let len = map.len(); - let mut result = String::with_capacity(len * 5 + 3); + let mut result = SmartString::new_const(); result.push_str("#{"); map.iter_mut().enumerate().for_each(|(i, (k, v))| { - use std::fmt::Write; - write!( result, "{:?}: {}{}", diff --git a/tests/data_size.rs b/tests/data_size.rs index a0415aa3..50f35f50 100644 --- a/tests/data_size.rs +++ b/tests/data_size.rs @@ -175,7 +175,7 @@ fn test_max_array_size() -> Result<(), Box> { assert_eq!( engine.eval::( " - let x = [1,2,3]; + let x = [1,2]; len([x, x, x]) " )?, diff --git a/tests/string.rs b/tests/string.rs index 3bbed302..37988600 100644 --- a/tests/string.rs +++ b/tests/string.rs @@ -332,7 +332,8 @@ fn test_string_split() -> Result<(), Box> { #[test] fn test_string_interpolated() -> Result<(), Box> { - let engine = Engine::new(); + // Make sure strings interpolation works even under raw + let engine = Engine::new_raw(); assert_eq!(engine.eval::("`${}`")?, ""); From bb1136e8ade9156e6360e3cc2ec8f9d802dd3e37 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Dec 2022 22:06:51 +0800 Subject: [PATCH 6/7] Refine what can be called in method style. --- CHANGELOG.md | 3 +- src/api/custom_syntax.rs | 4 +- src/func/call.rs | 21 +++------ src/parser.rs | 21 ++++----- src/tokenizer.rs | 96 ++++++++++++++++++++++------------------ src/types/fn_ptr.rs | 13 ++++-- 6 files changed, 81 insertions(+), 77 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b66ac38..135fc201 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ Bug fixes * Parsing deeply-nested closures (e.g. `||{||{||{||{||{||{||{...}}}}}}}`) no longer panics but will be confined to the nesting limit. * Closures containing a single expression are now allowed in `Engine::eval_expression` etc. * Strings interpolation now works under `Engine::new_raw` without any standard package. +* `Fn` now throws an error if the name is a reserved keyword as it cannot possibly map to such a function. This also disallows creating function pointers to custom operators which are defined as disabled keywords (a mouthful), but such custom operators are designed primarily to be used as operators. Breaking API changes -------------------- @@ -60,6 +61,7 @@ Net features * Array methods that take a function pointer, usually a closure (e.g. `map`, `filter`, `index_of`, `reduce` etc.), can now bind the array element to `this` when calling a closure. * This vastly improves performance when working with arrays of large types (e.g. object maps) by avoiding unnecessary cloning. +* `find` and `find_map` are added for arrays. * `for_each` is also added for arrays, allowing a closure to mutate array elements (bound to `this`) in turn. Enhancements @@ -79,7 +81,6 @@ Enhancements * `FnPtr::iter_curry` and `FnPtr::iter_curry_mut` are added. * `Dynamic::deep_scan` is added to recursively scan for `Dynamic` values. * `>>` and `<<` operators on integers no longer throw errors when the number of bits to shift is out of bounds. Shifting by a negative number of bits simply reverses the shift direction. -* `find` and `find_map` are added for arrays. Version 1.11.0 diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 87c793e6..bc3e8d60 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -4,7 +4,7 @@ use crate::ast::Expr; use crate::func::SendSync; use crate::parser::ParseResult; -use crate::tokenizer::{is_valid_identifier, Token, NO_TOKEN}; +use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_identifier, Token, NO_TOKEN}; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult, @@ -232,7 +232,7 @@ impl Engine { } let token = Token::lookup_symbol_from_syntax(s).unwrap_or_else(|| { - if Token::is_reserved_keyword(s) { + if is_reserved_keyword_or_symbol(s) { Token::Reserved(Box::new(s.into())) } else { NO_TOKEN diff --git a/src/func/call.rs b/src/func/call.rs index 197e4d27..61563777 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -575,14 +575,6 @@ impl Engine { _is_method_call: bool, pos: Position, ) -> RhaiResultOf<(Dynamic, bool)> { - fn no_method_err(name: &str, pos: Position) -> RhaiResultOf<(Dynamic, bool)> { - Err(ERR::ErrorRuntime( - format!("'{name}' should not be called this way. Try {name}(...);").into(), - pos, - ) - .into()) - } - // Check for data race. #[cfg(not(feature = "no_closure"))] ensure_no_data_race(fn_name, _args, is_ref_mut)?; @@ -622,16 +614,13 @@ impl Engine { // Handle is_shared() #[cfg(not(feature = "no_closure"))] - crate::engine::KEYWORD_IS_SHARED if _args.len() == 1 => { - return no_method_err(fn_name, pos) + crate::engine::KEYWORD_IS_SHARED => { + unreachable!("{} called as method", fn_name) } - KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR if _args.len() == 1 => { - return no_method_err(fn_name, pos) - } - - KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY if !_args.is_empty() => { - return no_method_err(fn_name, pos) + KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL + | KEYWORD_FN_PTR_CURRY => { + unreachable!("{} called as method", fn_name) } _ => (), diff --git a/src/parser.rs b/src/parser.rs index 8089c78d..7201fb81 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1657,7 +1657,7 @@ impl Engine { match input.peek().expect(NEVER_ENDS).0 { // Function call is allowed to have reserved keyword - Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s) => { + Token::LeftParen | Token::Bang | Token::Unit if is_keyword_function(&s).0 => { Expr::Variable( (None, ns, 0, state.get_interned_string(*s)).into(), None, @@ -1800,7 +1800,10 @@ impl Engine { state.allow_capture = false; } } - (Token::Reserved(s), ..) if is_keyword_function(s) => (), + (Token::Reserved(s), ..) if is_keyword_function(s).1 => (), + (Token::Reserved(s), pos) => { + return Err(PERR::Reserved(s.to_string()).into_err(*pos)) + } (.., pos) => return Err(PERR::PropertyExpected.into_err(*pos)), } @@ -2109,9 +2112,7 @@ impl Engine { } // lhs.module::id - syntax error #[cfg(not(feature = "no_module"))] - (.., Expr::Variable(x, ..)) if !x.1.is_empty() => { - Err(PERR::PropertyExpected.into_err(x.1.position())) - } + (.., Expr::Variable(x, ..)) if !x.1.is_empty() => unreachable!("lhs.ns::id"), // lhs.id (lhs, var_expr @ Expr::Variable(..)) => { let rhs = var_expr.into_property(state); @@ -2125,9 +2126,7 @@ impl Engine { )), // lhs.nnn::func(...) - syntax error #[cfg(not(feature = "no_module"))] - (.., Expr::FnCall(f, ..)) if f.is_qualified() => { - Err(PERR::PropertyExpected.into_err(f.namespace.position())) - } + (.., Expr::FnCall(f, ..)) if f.is_qualified() => unreachable!("lhs.ns::func()"), // lhs.Fn() or lhs.eval() (.., Expr::FnCall(f, func_pos)) if f.args.is_empty() @@ -2174,13 +2173,11 @@ impl Engine { match x.lhs { // lhs.module::id.dot_rhs or lhs.module::id[idx_rhs] - syntax error #[cfg(not(feature = "no_module"))] - Expr::Variable(x, ..) if !x.1.is_empty() => { - Err(PERR::PropertyExpected.into_err(x.1.position())) - } + Expr::Variable(x, ..) if !x.1.is_empty() => unreachable!("lhs.ns::id..."), // lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error #[cfg(not(feature = "no_module"))] Expr::FnCall(f, ..) if f.is_qualified() => { - Err(PERR::PropertyExpected.into_err(f.namespace.position())) + unreachable!("lhs.ns::func()...") } // lhs.id.dot_rhs or lhs.id[idx_rhs] Expr::Variable(..) | Expr::Property(..) => { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e180aedf..f9d0c87f 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -631,41 +631,6 @@ impl Token { }) } - /// Is a piece of syntax a reserved keyword? - #[must_use] - pub fn is_reserved_keyword(syntax: &str) -> bool { - match syntax { - #[cfg(feature = "no_object")] - "?." => true, - #[cfg(feature = "no_index")] - "?[" => true, - #[cfg(feature = "no_function")] - "fn" | "private" => true, - #[cfg(feature = "no_module")] - "import" | "export" | "as" => true, - - // List of reserved operators - "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" - | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => true, - - // 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" => true, - - 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 => { - true - } - - #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN => true, - - _ => false, - } - } - /// If another operator is after these, it's probably a unary operator /// (not sure about `fn` name). #[must_use] @@ -1994,7 +1959,8 @@ fn parse_identifier_token( if let Some(token) = Token::lookup_symbol_from_syntax(&identifier) { return (token, start_pos); } - if Token::is_reserved_keyword(&identifier) { + + if is_reserved_keyword_or_symbol(&identifier) { return (Token::Reserved(Box::new(identifier)), start_pos); } @@ -2008,18 +1974,27 @@ fn parse_identifier_token( (Token::Identifier(identifier.into()), start_pos) } -/// Is a keyword allowed as a function? +/// Can a keyword be called like a function? +/// +/// # Return values +/// +/// The first `bool` indicates whether the keyword can be called normally as a function. +/// +/// The second `bool` indicates whether the keyword can be called in method-call style. #[inline] #[must_use] -pub fn is_keyword_function(name: &str) -> bool { +pub fn is_keyword_function(name: &str) -> (bool, bool) { match name { - KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_TYPE_OF | KEYWORD_EVAL | KEYWORD_FN_PTR - | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY | KEYWORD_IS_DEF_VAR => true, + KEYWORD_TYPE_OF | KEYWORD_FN_PTR_CALL | KEYWORD_FN_PTR_CURRY => (true, true), + + KEYWORD_PRINT | KEYWORD_DEBUG | KEYWORD_EVAL | KEYWORD_FN_PTR | KEYWORD_IS_DEF_VAR => { + (true, false) + } #[cfg(not(feature = "no_function"))] - crate::engine::KEYWORD_IS_DEF_FN => true, + crate::engine::KEYWORD_IS_DEF_FN => (true, false), - _ => false, + _ => (false, false), } } @@ -2047,7 +2022,9 @@ pub fn is_valid_identifier(name: &str) -> bool { #[inline(always)] #[must_use] pub fn is_valid_function_name(name: &str) -> bool { - is_valid_identifier(name) && !is_keyword_function(name) + is_valid_identifier(name) + && !is_reserved_keyword_or_symbol(name) + && Token::lookup_symbol_from_syntax(name).is_none() } /// Is a character valid to start an identifier? @@ -2082,6 +2059,39 @@ pub const fn is_id_continue(x: char) -> bool { x.is_ascii_alphanumeric() || x == '_' } +/// Is a piece of syntax a reserved keyword or symbol? +#[must_use] +pub fn is_reserved_keyword_or_symbol(syntax: &str) -> bool { + match syntax { + #[cfg(feature = "no_object")] + "?." => true, + #[cfg(feature = "no_index")] + "?[" => true, + #[cfg(feature = "no_function")] + "fn" | "private" => true, + #[cfg(feature = "no_module")] + "import" | "export" | "as" => true, + + // List of reserved operators + "===" | "!==" | "->" | "<-" | "?" | ":=" | ":;" | "~" | "!." | "::<" | "(*" | "*)" + | "#" | "#!" | "@" | "$" | "++" | "--" | "..." | "<|" | "|>" => true, + + // 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" => true, + + 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 => true, + + #[cfg(not(feature = "no_function"))] + crate::engine::KEYWORD_IS_DEF_FN => true, + + _ => false, + } +} + /// _(internals)_ A type that implements the [`InputStream`] trait. /// Exported under the `internals` feature only. /// diff --git a/src/types/fn_ptr.rs b/src/types/fn_ptr.rs index 34dcc483..83539f83 100644 --- a/src/types/fn_ptr.rs +++ b/src/types/fn_ptr.rs @@ -2,11 +2,11 @@ use crate::eval::GlobalRuntimeState; use crate::func::EncapsulatedEnviron; -use crate::tokenizer::is_valid_function_name; +use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_function_name, Token}; use crate::types::dynamic::Variant; use crate::{ - Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, Position, RhaiError, - RhaiResult, RhaiResultOf, Shared, StaticVec, AST, ERR, + Dynamic, Engine, FnArgsVec, FuncArgs, ImmutableString, NativeCallContext, ParseErrorType, + Position, RhaiError, RhaiResult, RhaiResultOf, Shared, StaticVec, AST, ERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -536,6 +536,13 @@ impl TryFrom for FnPtr { #[cfg(not(feature = "no_function"))] fn_def: None, }) + } else if is_reserved_keyword_or_symbol(&value) + || Token::lookup_symbol_from_syntax(&value).is_some() + { + Err( + ERR::ErrorParsing(ParseErrorType::Reserved(value.to_string()), Position::NONE) + .into(), + ) } else { Err(ERR::ErrorFunctionNotFound(value.to_string(), Position::NONE).into()) } From 00c434eb714be1b7de6413d175ec832b5899a805 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 27 Dec 2022 22:51:38 +0800 Subject: [PATCH 7/7] Use Option instead of Token::NONE. --- src/api/custom_syntax.rs | 16 +++++----- src/ast/expr.rs | 9 +++--- src/eval/chaining.rs | 18 +++++------- src/eval/stmt.rs | 4 +-- src/func/call.rs | 63 ++++++++++++++++++++-------------------- src/func/native.rs | 4 +-- src/optimizer.rs | 6 ++-- src/parser.rs | 37 +++++++++++------------ src/tokenizer.rs | 3 -- 9 files changed, 77 insertions(+), 83 deletions(-) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index bc3e8d60..f881ff88 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -4,7 +4,7 @@ use crate::ast::Expr; use crate::func::SendSync; use crate::parser::ParseResult; -use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_identifier, Token, NO_TOKEN}; +use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_identifier, Token}; use crate::types::dynamic::Variant; use crate::{ Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult, @@ -231,11 +231,11 @@ impl Engine { continue; } - let token = Token::lookup_symbol_from_syntax(s).unwrap_or_else(|| { + let token = Token::lookup_symbol_from_syntax(s).or_else(|| { if is_reserved_keyword_or_symbol(s) { - Token::Reserved(Box::new(s.into())) + Some(Token::Reserved(Box::new(s.into()))) } else { - NO_TOKEN + None } }); @@ -256,13 +256,13 @@ impl Engine { #[cfg(not(feature = "no_float"))] CUSTOM_SYNTAX_MARKER_FLOAT if !segments.is_empty() => s.into(), // Standard or reserved keyword/symbol not in first position - _ if !segments.is_empty() && token != NO_TOKEN => { + _ if !segments.is_empty() && token.is_some() => { // Make it a custom keyword/symbol if it is disabled or reserved if (self .disabled_symbols .as_deref() .map_or(false, |m| m.contains(s)) - || token.is_reserved()) + || token.as_ref().map_or(false, Token::is_reserved)) && !self .custom_keywords .as_deref() @@ -276,7 +276,7 @@ impl Engine { } // Standard keyword in first position but not disabled _ if segments.is_empty() - && token.is_standard_keyword() + && token.as_ref().map_or(false, Token::is_standard_keyword) && !self .disabled_symbols .as_deref() @@ -298,7 +298,7 @@ impl Engine { .disabled_symbols .as_deref() .map_or(false, |m| m.contains(s)) - || (token.is_reserved() + || (token.as_ref().map_or(false, Token::is_reserved) && !self .custom_keywords .as_deref() diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 36299d83..bfb4a9ef 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -3,7 +3,7 @@ use super::{ASTFlags, ASTNode, Ident, Namespace, Stmt, StmtBlock}; use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE}; use crate::func::hashing::ALT_ZERO_HASH; -use crate::tokenizer::{Token, NO_TOKEN}; +use crate::tokenizer::Token; use crate::types::dynamic::Union; use crate::{ calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, SmartString, StaticVec, @@ -207,8 +207,7 @@ pub struct FnCallExpr { /// Does this function call capture the parent scope? pub capture_parent_scope: bool, /// Is this function call a native operator? - /// Otherwise set to [`Token::NONE`]. - pub op_token: Token, + pub op_token: Option, } impl fmt::Debug for FnCallExpr { @@ -222,7 +221,7 @@ impl fmt::Debug for FnCallExpr { ff.field("hash", &self.hashes) .field("name", &self.name) .field("args", &self.args); - if self.op_token != NO_TOKEN { + if self.op_token.is_some() { ff.field("op_token", &self.op_token); } if self.capture_parent_scope { @@ -582,7 +581,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, - op_token: NO_TOKEN, + op_token: None, } .into(), pos, diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 71d39a43..a31a4867 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -5,7 +5,6 @@ use super::{Caches, GlobalRuntimeState, Target}; use crate::ast::{ASTFlags, BinaryExpr, Expr, OpAssignment}; use crate::config::hashing::SusLock; use crate::engine::{FN_IDX_GET, FN_IDX_SET}; -use crate::tokenizer::NO_TOKEN; use crate::types::dynamic::Union; use crate::{ calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR, @@ -73,7 +72,7 @@ impl Engine { let hash = hash_idx().0; let args = &mut [target, idx]; - self.exec_native_fn_call(global, caches, FN_IDX_GET, NO_TOKEN, hash, args, true, pos) + self.exec_native_fn_call(global, caches, FN_IDX_GET, None, hash, args, true, pos) .map(|(r, ..)| r) } @@ -95,7 +94,7 @@ impl Engine { let args = &mut [target, idx, new_val]; self.exec_native_fn_call( - global, caches, FN_IDX_SET, NO_TOKEN, hash, args, is_ref_mut, pos, + global, caches, FN_IDX_SET, None, hash, args, is_ref_mut, pos, ) } @@ -751,8 +750,7 @@ impl Engine { let (mut orig_val, ..) = self .exec_native_fn_call( - global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut, - *pos, + global, caches, getter, None, *hash_get, args, is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -786,7 +784,7 @@ impl Engine { let args = &mut [target.as_mut(), &mut new_val]; self.exec_native_fn_call( - global, caches, setter, NO_TOKEN, *hash_set, args, is_ref_mut, *pos, + global, caches, setter, None, *hash_set, args, is_ref_mut, *pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -813,7 +811,7 @@ impl Engine { let args = &mut [target.as_mut()]; self.exec_native_fn_call( - global, caches, getter, NO_TOKEN, *hash_get, args, is_ref_mut, *pos, + global, caches, getter, None, *hash_get, args, is_ref_mut, *pos, ) .map_or_else( |err| match *err { @@ -904,8 +902,8 @@ impl Engine { // Assume getters are always pure let (mut val, ..) = self .exec_native_fn_call( - global, caches, getter, NO_TOKEN, *hash_get, args, - is_ref_mut, pos, + global, caches, getter, None, *hash_get, args, is_ref_mut, + pos, ) .or_else(|err| match *err { // Try an indexer if property does not exist @@ -940,7 +938,7 @@ impl Engine { // The return value is thrown away and not used. let _ = self .exec_native_fn_call( - global, caches, setter, NO_TOKEN, *hash_set, args, + global, caches, setter, None, *hash_set, args, is_ref_mut, pos, ) .or_else(|err| match *err { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 49627c08..8bb47110 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -159,7 +159,7 @@ impl Engine { let op_assign = op_assign_token.literal_syntax(); let op = op_token.literal_syntax(); - let token = op_assign_token.clone(); + let token = Some(op_assign_token.clone()); match self .exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos) @@ -168,7 +168,7 @@ impl Engine { Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) => { // Expand to `var = var op rhs` - let token = op_token.clone(); + let token = Some(op_token.clone()); *args[0] = self .exec_native_fn_call( diff --git a/src/func/call.rs b/src/func/call.rs index 61563777..b25568df 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -8,7 +8,7 @@ use crate::engine::{ KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{Caches, FnResolutionCacheEntry, GlobalRuntimeState}; -use crate::tokenizer::{is_valid_function_name, Token, NO_TOKEN}; +use crate::tokenizer::{is_valid_function_name, Token}; use crate::{ calc_fn_hash, calc_fn_hash_full, Dynamic, Engine, FnArgsVec, FnPtr, ImmutableString, OptimizationLevel, Position, RhaiError, RhaiResult, RhaiResultOf, Scope, Shared, ERR, @@ -164,7 +164,7 @@ impl Engine { _global: &GlobalRuntimeState, caches: &'s mut Caches, local_entry: &'s mut Option, - op_token: Token, + op_token: Option, hash_base: u64, args: Option<&mut FnCallArgs>, allow_dynamic: bool, @@ -270,31 +270,30 @@ impl Engine { } // Try to find a built-in version - let builtin = args.and_then(|args| match op_token { - Token::NONE => None, - token if token.is_op_assignment() => { - let (first_arg, rest_args) = args.split_first().unwrap(); + let builtin = + args.and_then(|args| match op_token { + None => None, + Some(token) if token.is_op_assignment() => { + let (first_arg, rest_args) = args.split_first().unwrap(); - get_builtin_op_assignment_fn(token, first_arg, rest_args[0]).map( - |(f, has_context)| FnResolutionCacheEntry { + get_builtin_op_assignment_fn(token, first_arg, rest_args[0]) + .map(|(f, has_context)| FnResolutionCacheEntry { + func: CallableFunction::Method { + func: Shared::new(f), + has_context, + }, + source: None, + }) + } + Some(token) => get_builtin_binary_op_fn(token, args[0], args[1]) + .map(|(f, has_context)| FnResolutionCacheEntry { func: CallableFunction::Method { func: Shared::new(f), has_context, }, source: None, - }, - ) - } - token => get_builtin_binary_op_fn(token, args[0], args[1]).map( - |(f, has_context)| FnResolutionCacheEntry { - func: CallableFunction::Method { - func: Shared::new(f), - has_context, - }, - source: None, - }, - ), - }); + }), + }); return if cache.filter.is_absent_and_set(hash) { // Do not cache "one-hit wonders" @@ -346,7 +345,7 @@ impl Engine { global: &mut GlobalRuntimeState, caches: &mut Caches, name: &str, - op_token: Token, + op_token: Option, hash: u64, args: &mut FnCallArgs, is_ref_mut: bool, @@ -568,7 +567,7 @@ impl Engine { caches: &mut Caches, _scope: Option<&mut Scope>, fn_name: &str, - op_token: Token, + op_token: Option, hashes: FnCallHashes, mut _args: &mut FnCallArgs, is_ref_mut: bool, @@ -634,7 +633,7 @@ impl Engine { let local_entry = &mut None; if let Some(FnResolutionCacheEntry { func, ref source }) = self - .resolve_fn(global, caches, local_entry, NO_TOKEN, hash, None, false) + .resolve_fn(global, caches, local_entry, None, hash, None, false) .cloned() { // Script function call @@ -801,7 +800,7 @@ impl Engine { caches, None, fn_name, - NO_TOKEN, + None, new_hash, args, false, @@ -888,7 +887,7 @@ impl Engine { caches, None, &fn_name, - NO_TOKEN, + None, new_hash, args, is_ref_mut, @@ -975,7 +974,7 @@ impl Engine { caches, None, fn_name, - NO_TOKEN, + None, hash, &mut args, is_ref_mut, @@ -1001,7 +1000,7 @@ impl Engine { scope: &mut Scope, mut this_ptr: Option<&mut Dynamic>, fn_name: &str, - op_token: Token, + op_token: Option, first_arg: Option<&Expr>, args_expr: &[Expr], hashes: FnCallHashes, @@ -1017,7 +1016,7 @@ impl Engine { let redirected; // Handle call() - Redirect function call match name { - _ if op_token != NO_TOKEN => (), + _ if op_token.is_some() => (), // Handle call(fn_ptr, ...) KEYWORD_FN_PTR_CALL if total_args >= 1 => { @@ -1571,7 +1570,7 @@ impl Engine { let op_token = op_token.clone(); // Short-circuit native unary operator call if under Fast Operators mode - if op_token == Token::Bang && self.fast_operators() && args.len() == 1 { + if op_token == Some(Token::Bang) && self.fast_operators() && args.len() == 1 { let mut value = self .get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])? .0 @@ -1587,7 +1586,7 @@ impl Engine { } // Short-circuit native binary operator call if under Fast Operators mode - if op_token != NO_TOKEN && self.fast_operators() && args.len() == 2 { + if op_token.is_some() && self.fast_operators() && args.len() == 2 { let mut lhs = self .get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &args[0])? .0 @@ -1601,7 +1600,7 @@ impl Engine { let operands = &mut [&mut lhs, &mut rhs]; if let Some((func, need_context)) = - get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1]) + get_builtin_binary_op_fn(op_token.clone().unwrap(), operands[0], operands[1]) { // Built-in found auto_restore! { let orig_level = global.level; global.level += 1 } diff --git a/src/func/native.rs b/src/func/native.rs index 7f0aba59..eaedd0ed 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -4,7 +4,7 @@ use super::call::FnCallArgs; use crate::ast::FnCallHashes; use crate::eval::{Caches, GlobalRuntimeState}; use crate::plugin::PluginFunction; -use crate::tokenizer::{is_valid_function_name, Token, TokenizeState, NO_TOKEN}; +use crate::tokenizer::{is_valid_function_name, Token, TokenizeState}; use crate::types::dynamic::Variant; use crate::{ calc_fn_hash, Dynamic, Engine, EvalContext, FuncArgs, Position, RhaiResult, RhaiResultOf, @@ -436,7 +436,7 @@ impl<'a> NativeCallContext<'a> { let caches = &mut Caches::new(); let fn_name = fn_name.as_ref(); - let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(NO_TOKEN); + let op_token = Token::lookup_symbol_from_syntax(fn_name); let args_len = args.len(); if native_only { diff --git a/src/optimizer.rs b/src/optimizer.rs index 0b8ff895..15d11d2c 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -147,7 +147,7 @@ impl<'a> OptimizerState<'a> { pub fn call_fn_with_constant_arguments( &mut self, fn_name: &str, - op_token: Token, + op_token: Option, arg_values: &mut [Dynamic], ) -> Option { self.engine @@ -1137,8 +1137,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) { return; } // Overloaded operators can override built-in. - _ if x.args.len() == 2 && x.op_token != Token::NONE && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => { - if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1]) + _ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => { + if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone().unwrap(), &arg_values[0], &arg_values[1]) .and_then(|(f, ctx)| { let context = if ctx { Some((state.engine, x.name.as_str(), None, &state.global, *pos).into()) diff --git a/src/parser.rs b/src/parser.rs index 7201fb81..39c51d67 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -12,7 +12,7 @@ use crate::eval::{Caches, GlobalRuntimeState}; use crate::func::{hashing::get_hasher, StraightHashMap}; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, - TokenizerControl, NO_TOKEN, + TokenizerControl, }; use crate::types::dynamic::AccessMode; use crate::types::StringsInterner; @@ -627,7 +627,7 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - op_token: NO_TOKEN, + op_token: None, namespace: _namespace, hashes, args, @@ -702,7 +702,7 @@ impl Engine { return Ok(FnCallExpr { name: state.get_interned_string(id), capture_parent_scope, - op_token: NO_TOKEN, + op_token: None, namespace: _namespace, hashes, args, @@ -1927,7 +1927,7 @@ impl Engine { name: state.get_interned_string("-"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)), args, - op_token: token, + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1955,7 +1955,7 @@ impl Engine { name: state.get_interned_string("+"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)), args, - op_token: token, + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1976,7 +1976,7 @@ impl Engine { name: state.get_interned_string("!"), hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)), args, - op_token: token, + op_token: Some(token), capture_parent_scope: false, } .into_fn_call_expr(pos)) @@ -1990,7 +1990,7 @@ impl Engine { /// Make an assignment statement. fn make_assignment_stmt( - op: Token, + op: Option, state: &mut ParseState, lhs: Expr, rhs: Expr, @@ -2023,10 +2023,10 @@ impl Engine { } } - let op_info = if op == NO_TOKEN { - OpAssignment::new_assignment(op_pos) - } else { + let op_info = if let Some(op) = op { OpAssignment::new_op_assignment_from_token(op, op_pos) + } else { + OpAssignment::new_assignment(op_pos) }; match lhs { @@ -2307,9 +2307,9 @@ impl Engine { let hash = calc_fn_hash(None, &op, 2); let is_valid_script_function = is_valid_function_name(&op); let operator_token = if is_valid_script_function { - NO_TOKEN + None } else { - op_token.clone() + Some(op_token.clone()) }; let mut args = StaticVec::new_const(); @@ -2380,7 +2380,7 @@ impl Engine { name: state.get_interned_string(op), hashes: FnCallHashes::from_native(calc_fn_hash(None, op, 1)), args, - op_token: Token::Bang, + op_token: Some(Token::Bang), capture_parent_scope: false, }; not_base.into_fn_call_expr(pos) @@ -3183,11 +3183,12 @@ impl Engine { let (op, pos) = match input.peek().expect(NEVER_ENDS) { // var = ... - (Token::Equals, ..) => (NO_TOKEN, eat_token(input, Token::Equals)), + (Token::Equals, ..) => (None, eat_token(input, Token::Equals)), // var op= ... - (token, ..) if token.is_op_assignment() => { - input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS) - } + (token, ..) if token.is_op_assignment() => input + .next() + .map(|(op, pos)| (Some(op), pos)) + .expect(NEVER_ENDS), // Not op-assignment _ => return Ok(Stmt::Expr(expr.into())), }; @@ -3683,7 +3684,7 @@ impl Engine { num_externals + 1, )), args, - op_token: NO_TOKEN, + op_token: None, capture_parent_scope: false, } .into_fn_call_expr(pos); diff --git a/src/tokenizer.rs b/src/tokenizer.rs index f9d0c87f..1f309386 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -55,9 +55,6 @@ type LERR = LexError; /// Separator character for numbers. const NUMBER_SEPARATOR: char = '_'; -/// No token. -pub const NO_TOKEN: Token = Token::NONE; - /// A stream of tokens. pub type TokenStream<'a> = Peekable>;