From e06c2b2abb0a894530a2b948a388b5b60ed120d4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 5 Mar 2022 12:06:47 +0800 Subject: [PATCH 1/8] Remove constants in function call expressions. --- CHANGELOG.md | 1 + src/ast/expr.rs | 33 ++---------------- src/eval/chaining.rs | 15 +++----- src/eval/expr.rs | 8 ++--- src/func/call.rs | 83 +++++++++++++++++++------------------------- src/optimizer.rs | 59 ++++++++++++++++--------------- src/parser.rs | 2 -- 7 files changed, 77 insertions(+), 124 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ac63f7ed..bc700e7c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -28,6 +28,7 @@ Enhancements * Variable definitions are optimized so that shadowed variables are reused as much as possible to reduce memory consumption. * `FnAccess` and `FnNamespace` now implement `Ord` and `PartialOrd`. * The `event_handler_map` example is enhanced to prevent shadowing of the state object map. +* Separation of constants in function calls is removed as its performance benefit is dubious. Version 1.5.0 diff --git a/src/ast/expr.rs b/src/ast/expr.rs index af352bdf..a72b2540 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -176,17 +176,6 @@ pub struct FnCallExpr { pub hashes: FnCallHashes, /// List of function call argument expressions. pub args: StaticVec, - /// List of function call arguments that are constants. - /// - /// Any arguments in `args` that is [`Expr::Stack`] indexes into this - /// array to find the constant for use as its argument value. - /// - /// # Notes - /// - /// Constant arguments are very common in function calls, and keeping each constant in - /// an [`Expr::DynamicConstant`] involves an additional allocation. Keeping the constant - /// values in an inlined array avoids these extra allocations. - pub constants: StaticVec, /// Does this function call capture the parent scope? pub capture_parent_scope: bool, /// [Position] of the function name. @@ -206,9 +195,6 @@ impl fmt::Debug for FnCallExpr { ff.field("hash", &self.hashes) .field("name", &self.name) .field("args", &self.args); - if !self.constants.is_empty() { - ff.field("constants", &self.constants); - } ff.field("pos", &self.pos); ff.finish() } @@ -409,12 +395,6 @@ pub enum Expr { ), /// xxx `.` method `(` expr `,` ... `)` MethodCall(Box, Position), - /// Stack slot for function calls. See [`FnCallExpr`] for more details. - /// - /// This variant does not map to any language structure. It is used in function calls with - /// constant arguments where the `usize` number indexes into an array containing a list of - /// constant arguments for the function call. - Stack(usize, Position), /// { [statement][Stmt] ... } Stmt(Box), /// func `(` expr `,` ... `)` @@ -490,7 +470,6 @@ impl fmt::Debug for Expr { } Self::Property(x, ..) => write!(f, "Property({})", x.2), Self::MethodCall(x, ..) => f.debug_tuple("MethodCall").field(x).finish(), - Self::Stack(x, ..) => write!(f, "ConstantArg[{}]", x), Self::Stmt(x) => { let pos = x.span(); if !pos.is_none() { @@ -645,8 +624,7 @@ impl Expr { namespace: None, name: KEYWORD_FN_PTR.into(), hashes: calc_fn_hash(f.fn_name(), 1).into(), - args: once(Self::Stack(0, pos)).collect(), - constants: once(f.fn_name().into()).collect(), + args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), capture_parent_scope: false, pos, } @@ -704,7 +682,6 @@ impl Expr { | Self::Array(.., pos) | Self::Map(.., pos) | Self::Variable(.., pos, _) - | Self::Stack(.., pos) | Self::And(.., pos) | Self::Or(.., pos) | Self::Index(.., pos) @@ -759,7 +736,6 @@ impl Expr { | Self::Dot(.., pos) | Self::Index(.., pos) | Self::Variable(.., pos, _) - | Self::Stack(.., pos) | Self::FnCall(.., pos) | Self::MethodCall(.., pos) | Self::Custom(.., pos) @@ -786,7 +762,7 @@ impl Expr { Self::Stmt(x) => x.iter().all(Stmt::is_pure), - Self::Variable(..) | Self::Stack(..) => true, + Self::Variable(..) => true, _ => self.is_constant(), } @@ -810,8 +786,7 @@ impl Expr { | Self::IntegerConstant(..) | Self::CharConstant(..) | Self::StringConstant(..) - | Self::Unit(..) - | Self::Stack(..) => true, + | Self::Unit(..) => true, Self::InterpolatedString(x, ..) | Self::Array(x, ..) => x.iter().all(Self::is_constant), @@ -872,8 +847,6 @@ impl Expr { Token::LeftParen => true, _ => false, }, - - Self::Stack(..) => false, } } /// Recursively walk this expression. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 8b87e79c..61e400c7 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -684,16 +684,13 @@ impl Engine { Expr::MethodCall(x, ..) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { - let crate::ast::FnCallExpr { - args, constants, .. - } = x.as_ref(); + let crate::ast::FnCallExpr { args, .. } = x.as_ref(); let (values, pos) = args.iter().try_fold( (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| { - let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, expr, constants, level, - )?; + let (value, arg_pos) = + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?; if values.is_empty() { pos = arg_pos; } @@ -732,15 +729,13 @@ impl Engine { Expr::MethodCall(x, ..) if _parent_chain_type == ChainType::Dotting && !x.is_qualified() => { - let crate::ast::FnCallExpr { - args, constants, .. - } = x.as_ref(); + let crate::ast::FnCallExpr { args, .. } = x.as_ref(); let (values, pos) = args.iter().try_fold( (crate::FnArgsVec::with_capacity(args.len()), Position::NONE), |(mut values, mut pos), expr| { let (value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, expr, constants, level, + scope, global, state, lib, this_ptr, expr, level, )?; if values.is_empty() { pos = arg_pos diff --git a/src/eval/expr.rs b/src/eval/expr.rs index b2a155b3..af479490 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -212,7 +212,6 @@ impl Engine { capture_parent_scope: capture, hashes, args, - constants, .. } = expr; @@ -222,8 +221,7 @@ impl Engine { let hash = hashes.native; return self.make_qualified_function_call( - scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos, - level, + scope, global, state, lib, this_ptr, namespace, name, args, hash, pos, level, ); } @@ -234,8 +232,8 @@ impl Engine { ); self.make_function_call( - scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, - *capture, pos, level, + scope, global, state, lib, this_ptr, name, first_arg, args, *hashes, *capture, pos, + level, ) } diff --git a/src/func/call.rs b/src/func/call.rs index dc3090af..37083454 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -958,35 +958,30 @@ impl Engine { lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, arg_expr: &Expr, - constants: &[Dynamic], level: usize, ) -> RhaiResultOf<(Dynamic, Position)> { - Ok(( - if let Expr::Stack(slot, ..) = arg_expr { + #[cfg(feature = "debugging")] + if self.debugger.is_some() { + if let Some(value) = arg_expr.get_literal_value() { #[cfg(feature = "debugging")] self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; - constants[*slot].clone() - } else if let Some(value) = arg_expr.get_literal_value() { - #[cfg(feature = "debugging")] - self.run_debugger(scope, global, state, lib, this_ptr, arg_expr, level)?; - value - } else { - // Do not match function exit for arguments - #[cfg(feature = "debugging")] - let reset_debugger = global.debugger.clear_status_if(|status| { - matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) - }); + return Ok((value, arg_expr.start_position())); + } + } - let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level); + // Do not match function exit for arguments + #[cfg(feature = "debugging")] + let reset_debugger = global.debugger.clear_status_if(|status| { + matches!(status, crate::eval::DebuggerStatus::FunctionExit(..)) + }); - // Restore function exit status - #[cfg(feature = "debugging")] - global.debugger.reset_status(reset_debugger); + let result = self.eval_expr(scope, global, state, lib, this_ptr, arg_expr, level); - result? - }, - arg_expr.start_position(), - )) + // Restore function exit status + #[cfg(feature = "debugging")] + global.debugger.reset_status(reset_debugger); + + Ok((result?, arg_expr.start_position())) } /// Call a function in normal function-call style. @@ -1000,7 +995,6 @@ impl Engine { fn_name: &str, first_arg: Option<&Expr>, args_expr: &[Expr], - constants: &[Dynamic], hashes: FnCallHashes, capture_scope: bool, pos: Position, @@ -1019,7 +1013,7 @@ impl Engine { KEYWORD_FN_PTR_CALL if total_args >= 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; if !arg_value.is::() { return Err(self.make_type_mismatch_err::( @@ -1054,7 +1048,7 @@ impl Engine { KEYWORD_FN_PTR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; // Fn - only in function call style return arg_value @@ -1068,8 +1062,8 @@ impl Engine { // Handle curry() KEYWORD_FN_PTR_CURRY if total_args > 1 => { let first = first_arg.unwrap(); - let (arg_value, arg_pos) = self - .get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?; + let (arg_value, arg_pos) = + self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?; if !arg_value.is::() { return Err(self.make_type_mismatch_err::( @@ -1082,9 +1076,8 @@ impl Engine { // Append the new curried arguments to the existing list. let fn_curry = a_expr.iter().try_fold(fn_curry, |mut curried, expr| { - let (value, ..) = self.get_arg_value( - scope, global, state, lib, this_ptr, expr, constants, level, - )?; + let (value, ..) = + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level)?; curried.push(value); Ok::<_, RhaiError>(curried) })?; @@ -1097,7 +1090,7 @@ impl Engine { crate::engine::KEYWORD_IS_SHARED if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, ..) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; return Ok(arg_value.is_shared().into()); } @@ -1105,16 +1098,15 @@ impl Engine { #[cfg(not(feature = "no_function"))] crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => { let first = first_arg.unwrap(); - let (arg_value, arg_pos) = self - .get_arg_value(scope, global, state, lib, this_ptr, first, constants, level)?; + let (arg_value, arg_pos) = + self.get_arg_value(scope, global, state, lib, this_ptr, first, level)?; let fn_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; - let (arg_value, arg_pos) = self.get_arg_value( - scope, global, state, lib, this_ptr, &a_expr[0], constants, level, - )?; + let (arg_value, arg_pos) = + self.get_arg_value(scope, global, state, lib, this_ptr, &a_expr[0], level)?; let num_params = arg_value .as_int() @@ -1133,7 +1125,7 @@ impl Engine { KEYWORD_IS_DEF_VAR if total_args == 1 => { let arg = first_arg.unwrap(); let (arg_value, arg_pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; let var_name = arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, arg_pos))?; @@ -1146,7 +1138,7 @@ impl Engine { let orig_scope_len = scope.len(); let arg = first_arg.unwrap(); let (arg_value, pos) = - self.get_arg_value(scope, global, state, lib, this_ptr, arg, constants, level)?; + self.get_arg_value(scope, global, state, lib, this_ptr, arg, level)?; let script = &arg_value .into_immutable_string() .map_err(|typ| self.make_type_mismatch_err::(typ, pos))?; @@ -1195,7 +1187,7 @@ impl Engine { .map(|&v| v) .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); @@ -1227,7 +1219,7 @@ impl Engine { // func(x, ...) -> x.func(...) a_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; @@ -1262,10 +1254,8 @@ impl Engine { .into_iter() .chain(a_expr.iter()) .try_for_each(|expr| { - self.get_arg_value( - scope, global, state, lib, this_ptr, expr, constants, level, - ) - .map(|(value, ..)| arg_values.push(value.flatten())) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) + .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(curry.iter_mut()); args.extend(arg_values.iter_mut()); @@ -1290,7 +1280,6 @@ impl Engine { namespace: &crate::module::Namespace, fn_name: &str, args_expr: &[Expr], - constants: &[Dynamic], hash: u64, pos: Position, level: usize, @@ -1313,7 +1302,7 @@ impl Engine { arg_values.push(Dynamic::UNIT); args_expr.iter().skip(1).try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; @@ -1344,7 +1333,7 @@ impl Engine { } else { // func(..., ...) or func(mod::x, ...) args_expr.iter().try_for_each(|expr| { - self.get_arg_value(scope, global, state, lib, this_ptr, expr, constants, level) + self.get_arg_value(scope, global, state, lib, this_ptr, expr, level) .map(|(value, ..)| arg_values.push(value.flatten())) })?; args.extend(arg_values.iter_mut()); diff --git a/src/optimizer.rs b/src/optimizer.rs index fc37aebe..2aeb695b 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -438,15 +438,7 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b Expr::FnCall(ref mut x2, ..) => { state.set_dirty(); x.0 = Some(OpAssignment::new_from_base(&x2.name)); - - let value = mem::take(&mut x2.args[1]); - - if let Expr::Stack(slot, pos) = value { - x.1.rhs = - Expr::from_dynamic(mem::take(x2.constants.get_mut(slot).unwrap()), pos); - } else { - x.1.rhs = value; - } + x.1.rhs = mem::take(&mut x2.args[1]); } ref expr => unreachable!("Expr::FnCall expected but gets {:?}", expr), } @@ -1063,7 +1055,6 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { && x.args[0].is_constant() => { let fn_name = match x.args[0] { - Expr::Stack(slot, ..) => x.constants[slot].clone(), Expr::StringConstant(ref s, ..) => s.clone().into(), _ => Dynamic::UNIT }; @@ -1088,11 +1079,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { && x.args.iter().all(Expr::is_constant) // all arguments are constants //&& !is_valid_identifier(x.name.chars()) // cannot be scripted => { - let arg_values = &mut x.args.iter().map(|e| match e { - Expr::Stack(slot, ..) => x.constants[*slot].clone(), - _ => e.get_literal_value().unwrap() - }).collect::>(); - + let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::>(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); match x.name.as_str() { @@ -1131,14 +1118,21 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); // Move constant arguments - let constants = &mut x.constants; - x.args.iter_mut().for_each(|arg| { - if let Some(value) = arg.get_literal_value() { - state.set_dirty(); - constants.push(value); - *arg = Expr::Stack(constants.len()-1, arg.start_position()); + for arg in x.args.iter_mut() { + match arg { + Expr::DynamicConstant(..) | Expr::Unit(..) + | Expr::StringConstant(..) | Expr::CharConstant(..) + | Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (), + + #[cfg(not(feature = "no_float"))] + Expr:: FloatConstant(..) => (), + + _ => if let Some(value) = arg.get_literal_value() { + state.set_dirty(); + *arg = Expr::DynamicConstant(value.into(), arg.start_position()); + }, } - }); + } } // Eagerly call functions @@ -1154,10 +1148,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { let has_script_fn = false; if !has_script_fn { - let arg_values = &mut x.args.iter().map(|e| match e { - Expr::Stack(slot, ..) => x.constants[*slot].clone(), - _ => e.get_literal_value().unwrap() - }).collect::>(); + let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::>(); let result = match x.name.as_str() { KEYWORD_TYPE_OF if arg_values.len() == 1 => Some(state.engine.map_type_name(arg_values[0].type_name()).into()), @@ -1181,10 +1172,18 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { optimize_expr(arg, state, false); // Move constant arguments - if let Some(value) = arg.get_literal_value() { - state.set_dirty(); - x.constants.push(value); - *arg = Expr::Stack(x.constants.len()-1, arg.start_position()); + match arg { + Expr::DynamicConstant(..) | Expr::Unit(..) + | Expr::StringConstant(..) | Expr::CharConstant(..) + | Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (), + + #[cfg(not(feature = "no_float"))] + Expr:: FloatConstant(..) => (), + + _ => if let Some(value) = arg.get_literal_value() { + state.set_dirty(); + *arg = Expr::DynamicConstant(value.into(), arg.start_position()); + }, } }, diff --git a/src/parser.rs b/src/parser.rs index f96c2e1c..2a8949d5 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -521,7 +521,6 @@ impl Engine { namespace, hashes, args, - constants: StaticVec::new_const(), pos: settings.pos, } .into_fn_call_expr(settings.pos)); @@ -586,7 +585,6 @@ impl Engine { namespace, hashes, args, - constants: StaticVec::new_const(), pos: settings.pos, } .into_fn_call_expr(settings.pos)); From 8bda8c64df50a3af7844fd174a334e889fa179fb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 5 Mar 2022 17:57:23 +0800 Subject: [PATCH 2/8] Refine data structures --- src/api/custom_syntax.rs | 8 +- src/ast/expr.rs | 52 ++++----- src/ast/ident.rs | 29 ++++- src/ast/mod.rs | 3 + src/{module => ast}/namespace.rs | 28 +++-- src/ast/stmt.rs | 6 +- src/eval/chaining.rs | 4 +- src/eval/expr.rs | 31 +++--- src/eval/stmt.rs | 43 ++++---- src/func/call.rs | 12 +- src/lib.rs | 8 +- src/module/mod.rs | 5 - src/optimizer.rs | 26 ++--- src/packages/lang_core.rs | 16 ++- src/parser.rs | 183 ++++++++++++++----------------- 15 files changed, 235 insertions(+), 219 deletions(-) rename src/{module => ast}/namespace.rs (86%) diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index 1fcb0455..d3411fce 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -72,8 +72,8 @@ impl Expression<'_> { pub fn get_string_value(&self) -> Option<&str> { match self.0 { #[cfg(not(feature = "no_module"))] - Expr::Variable(.., x) if x.1.is_some() => None, - Expr::Variable(.., x) => Some(x.2.as_str()), + Expr::Variable(x, ..) if !x.1.is_empty() => None, + Expr::Variable(x, ..) => Some(x.3.as_str()), Expr::StringConstant(x, ..) => Some(x.as_str()), _ => None, } @@ -102,8 +102,8 @@ impl Expression<'_> { Expr::CharConstant(x, ..) => reify!(*x => Option), Expr::StringConstant(x, ..) => reify!(x.clone() => Option), - Expr::Variable(.., x) => { - let x: ImmutableString = x.2.clone().into(); + Expr::Variable(x, ..) => { + let x: ImmutableString = x.3.clone().into(); reify!(x => Option) } Expr::BoolConstant(x, ..) => reify!(*x => Option), diff --git a/src/ast/expr.rs b/src/ast/expr.rs index a72b2540..df17cc59 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -169,7 +169,7 @@ impl FnCallHashes { pub struct FnCallExpr { /// Namespace of the function, if any. #[cfg(not(feature = "no_module"))] - pub namespace: Option, + pub namespace: super::Namespace, /// Function name. pub name: Identifier, /// Pre-calculated hashes. @@ -186,8 +186,8 @@ impl fmt::Debug for FnCallExpr { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut ff = f.debug_struct("FnCallExpr"); #[cfg(not(feature = "no_module"))] - if let Some(ref ns) = self.namespace { - ff.field("namespace", ns); + if !self.namespace.is_empty() { + ff.field("namespace", &self.namespace); } if self.capture_parent_scope { ff.field("capture_parent_scope", &self.capture_parent_scope); @@ -206,9 +206,9 @@ impl FnCallExpr { /// Always `false` under `no_module`. #[inline(always)] #[must_use] - pub const fn is_qualified(&self) -> bool { + pub fn is_qualified(&self) -> bool { #[cfg(not(feature = "no_module"))] - return self.namespace.is_some(); + return !self.namespace.is_empty(); #[cfg(feature = "no_module")] return false; } @@ -372,21 +372,17 @@ pub enum Expr { ), /// () Unit(Position), - /// Variable access - optional short index, position, (optional index, optional (hash, modules), variable name) + /// Variable access - (optional long index, namespace, namespace hash, variable name), optional short index, position /// /// The short index is [`u8`] which is used when the index is <= 255, which should be the vast /// majority of cases (unless there are more than 255 variables defined!). /// This is to avoid reading a pointer redirection during each variable access. Variable( - Option, - Position, #[cfg(not(feature = "no_module"))] - Box<( - Option, - Option<(crate::module::Namespace, u64)>, - Identifier, - )>, + Box<(Option, super::Namespace, u64, Identifier)>, + Option, #[cfg(feature = "no_module")] Box<(Option, (), Identifier)>, + Position, ), /// Property access - ((getter, hash), (setter, hash), prop) Property( @@ -451,18 +447,18 @@ impl fmt::Debug for Expr { .entries(x.0.iter().map(|(k, v)| (k, v))) .finish() } - Self::Variable(i, _, x) => { + Self::Variable(x, i, ..) => { f.write_str("Variable(")?; #[cfg(not(feature = "no_module"))] - if let Some((ref namespace, ..)) = x.1 { - write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())?; - let pos = namespace.position(); + if !x.1.is_empty() { + write!(f, "{}{}", x.1, Token::DoubleColon.literal_syntax())?; + let pos = x.1.position(); if !pos.is_none() { display_pos = format!(" @ {:?}", pos); } } - f.write_str(&x.2)?; + f.write_str(&x.3)?; if let Some(n) = i.map_or_else(|| x.0, |n| NonZeroUsize::new(n.get() as usize)) { write!(f, " #{}", n)?; } @@ -621,7 +617,7 @@ impl Expr { Union::FnPtr(f, ..) if !f.is_curried() => Self::FnCall( FnCallExpr { #[cfg(not(feature = "no_module"))] - namespace: None, + namespace: super::Namespace::NONE, name: KEYWORD_FN_PTR.into(), hashes: calc_fn_hash(f.fn_name(), 1).into(), args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(), @@ -640,12 +636,12 @@ impl Expr { /// `non_qualified` is ignored under `no_module`. #[inline] #[must_use] - pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool { + pub(crate) fn is_variable_access(&self, non_qualified: bool) -> bool { let _non_qualified = non_qualified; match self { #[cfg(not(feature = "no_module"))] - Self::Variable(.., x) if _non_qualified && x.1.is_some() => false, + Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => false, Self::Variable(..) => true, _ => false, } @@ -660,8 +656,8 @@ impl Expr { match self { #[cfg(not(feature = "no_module"))] - Self::Variable(.., x) if _non_qualified && x.1.is_some() => None, - Self::Variable(.., x) => Some(x.2.as_str()), + Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => None, + Self::Variable(x, ..) => Some(x.3.as_str()), _ => None, } } @@ -681,7 +677,7 @@ impl Expr { | Self::StringConstant(.., pos) | Self::Array(.., pos) | Self::Map(.., pos) - | Self::Variable(.., pos, _) + | Self::Variable(.., pos) | Self::And(.., pos) | Self::Or(.., pos) | Self::Index(.., pos) @@ -702,9 +698,9 @@ impl Expr { pub fn start_position(&self) -> Position { match self { #[cfg(not(feature = "no_module"))] - Self::Variable(.., x) => { - if let Some((ref namespace, ..)) = x.1 { - namespace.position() + Self::Variable(x, ..) => { + if !x.1.is_empty() { + x.1.position() } else { self.position() } @@ -735,7 +731,7 @@ impl Expr { | Self::Or(.., pos) | Self::Dot(.., pos) | Self::Index(.., pos) - | Self::Variable(.., pos, _) + | Self::Variable(.., pos) | Self::FnCall(.., pos) | Self::MethodCall(.., pos) | Self::Custom(.., pos) diff --git a/src/ast/ident.rs b/src/ast/ident.rs index 4a19878d..4fb0d3f2 100644 --- a/src/ast/ident.rs +++ b/src/ast/ident.rs @@ -3,7 +3,11 @@ use crate::{Identifier, Position}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{fmt, hash::Hash}; +use std::{ + fmt, + hash::Hash, + ops::{Deref, DerefMut}, +}; /// _(internals)_ An identifier containing a name and a [position][Position]. /// Exported under the `internals` feature only. @@ -29,7 +33,30 @@ impl AsRef for Ident { } } +impl Deref for Ident { + type Target = Identifier; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.name + } +} + +impl DerefMut for Ident { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.name + } +} + impl Ident { + /// An empty [`Ident`]. + pub const EMPTY: Self = Self { + name: Identifier::new_const(), + pos: Position::NONE, + }; + + /// Get the name of the identifier as a string slice. #[inline(always)] pub fn as_str(&self) -> &str { self.name.as_str() diff --git a/src/ast/mod.rs b/src/ast/mod.rs index 16be3bb9..4a22de90 100644 --- a/src/ast/mod.rs +++ b/src/ast/mod.rs @@ -4,6 +4,7 @@ pub mod ast; pub mod expr; pub mod flags; pub mod ident; +pub mod namespace; pub mod script_fn; pub mod stmt; @@ -12,6 +13,8 @@ pub use expr::{BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes}; pub use flags::{ASTFlags, FnAccess}; pub use ident::Ident; #[cfg(not(feature = "no_module"))] +pub use namespace::Namespace; +#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] pub use script_fn::EncapsulatedEnviron; #[cfg(not(feature = "no_function"))] diff --git a/src/module/namespace.rs b/src/ast/namespace.rs similarity index 86% rename from src/module/namespace.rs rename to src/ast/namespace.rs index 63595c29..22103d5d 100644 --- a/src/module/namespace.rs +++ b/src/ast/namespace.rs @@ -30,6 +30,10 @@ pub struct Namespace { impl fmt::Debug for Namespace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_empty() { + return f.write_str("NONE"); + } + if let Some(index) = self.index { write!(f, "{} -> ", index)?; } @@ -38,7 +42,7 @@ impl fmt::Debug for Namespace { &self .path .iter() - .map(|Ident { name, .. }| name.as_str()) + .map(|m| m.as_str()) .collect::>() .join(Token::DoubleColon.literal_syntax()), ) @@ -47,11 +51,15 @@ impl fmt::Debug for Namespace { impl fmt::Display for Namespace { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if self.is_empty() { + return Ok(()); + } + f.write_str( &self .path .iter() - .map(|Ident { name, .. }| name.as_str()) + .map(|m| m.as_str()) .collect::>() .join(Token::DoubleColon.literal_syntax()), ) @@ -94,14 +102,20 @@ impl From> for Namespace { } impl Namespace { + /// Constant for no namespace. + pub const NONE: Self = Self { + index: None, + path: StaticVec::new_const(), + }; + /// Create a new [`Namespace`]. #[inline(always)] #[must_use] - pub const fn new() -> Self { - Self { - index: None, - path: StaticVec::new_const(), - } + pub fn new(root: impl Into) -> Self { + let mut path = StaticVec::new_const(); + path.push(root.into()); + + Self { index: None, path } } /// Get the [`Scope`][crate::Scope] index offset. #[inline(always)] diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index ae6d5353..9ef68e10 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -127,7 +127,7 @@ pub struct TryCatchBlock { /// `try` block. pub try_block: StmtBlock, /// `catch` variable, if any. - pub catch_var: Option, + pub catch_var: Ident, /// `catch` block. pub catch_block: StmtBlock, } @@ -346,7 +346,7 @@ pub enum Stmt { /// * [`NEGATED`][ASTFlags::NEGATED] = `until` Do(Box<(Expr, StmtBlock)>, ASTFlags, Position), /// `for` `(` id `,` counter `)` `in` expr `{` stmt `}` - For(Box<(Ident, Option, Expr, StmtBlock)>, Position), + For(Box<(Ident, Ident, Expr, StmtBlock)>, Position), /// \[`export`\] `let`|`const` id `=` expr /// /// ### Flags @@ -385,7 +385,7 @@ pub enum Stmt { /// /// Not available under `no_module`. #[cfg(not(feature = "no_module"))] - Import(Box<(Expr, Option)>, Position), + Import(Box<(Expr, Ident)>, Position), /// `export` var `as` alias /// /// Not available under `no_module`. diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 61e400c7..3f9718f2 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -620,7 +620,7 @@ impl Engine { match lhs { // id.??? or id[???] - Expr::Variable(.., var_pos, x) => { + Expr::Variable(x, .., var_pos) => { #[cfg(feature = "debugging")] self.run_debugger(scope, global, state, lib, this_ptr, lhs, level)?; @@ -631,7 +631,7 @@ impl Engine { self.search_namespace(scope, global, state, lib, this_ptr, lhs, level)?; let obj_ptr = &mut target; - let root = (x.2.as_str(), *var_pos); + let root = (x.3.as_str(), *var_pos); self.eval_dot_index_chain_helper( global, state, lib, &mut None, obj_ptr, root, expr, rhs, options, idx_values, diff --git a/src/eval/expr.rs b/src/eval/expr.rs index af479490..f824ac7b 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -18,8 +18,10 @@ impl Engine { &self, global: &GlobalRuntimeState, state: &mut EvalState, - namespace: &crate::module::Namespace, + namespace: &crate::ast::Namespace, ) -> Option> { + assert!(!namespace.is_empty()); + let root = namespace.root(); // Qualified - check if the root module is directly indexed @@ -53,23 +55,23 @@ impl Engine { level: usize, ) -> RhaiResultOf<(Target<'s>, Position)> { match expr { - Expr::Variable(Some(_), ..) => { + Expr::Variable(_, Some(_), _) => { self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) } - Expr::Variable(None, _var_pos, v) => match v.as_ref() { + Expr::Variable(v, None, _var_pos) => match v.as_ref() { // Normal variable access #[cfg(not(feature = "no_module"))] - (_, None, _) => { + (_, ns, ..) if ns.is_empty() => { self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) } #[cfg(feature = "no_module")] - (_, (), _) => { + (_, (), ..) => { self.search_scope_only(scope, global, state, lib, this_ptr, expr, level) } // Qualified variable access #[cfg(not(feature = "no_module"))] - (_, Some((namespace, hash_var)), var_name) => { + (_, namespace, hash_var, var_name) => { // foo:bar::baz::VARIABLE if let Some(module) = self.search_imports(global, state, namespace) { return if let Some(mut target) = module.get_qualified_var(*hash_var) { @@ -139,7 +141,7 @@ impl Engine { let (index, var_pos) = match expr { // Check if the variable is `this` - Expr::Variable(None, pos, v) if v.0.is_none() && v.2 == KEYWORD_THIS => { + Expr::Variable(v, None, pos) if v.0.is_none() && v.3 == KEYWORD_THIS => { return if let Some(val) = this_ptr { Ok(((*val).into(), *pos)) } else { @@ -147,8 +149,8 @@ impl Engine { } } _ if state.always_search_scope => (0, expr.start_position()), - Expr::Variable(Some(i), pos, ..) => (i.get() as usize, *pos), - Expr::Variable(None, pos, v) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), + Expr::Variable(.., Some(i), pos) => (i.get() as usize, *pos), + Expr::Variable(v, None, pos) => (v.0.map(NonZeroUsize::get).unwrap_or(0), *pos), _ => unreachable!("Expr::Variable expected but gets {:?}", expr), }; @@ -216,7 +218,7 @@ impl Engine { } = expr; #[cfg(not(feature = "no_module"))] - if let Some(namespace) = namespace.as_ref() { + if !namespace.is_empty() { // Qualified function call let hash = hashes.native; @@ -280,14 +282,14 @@ impl Engine { // Then variable access. // We shouldn't do this for too many variants because, soon or later, the added comparisons // will cost more than the mis-predicted `match` branch. - if let Expr::Variable(index, var_pos, x) = expr { + if let Expr::Variable(x, index, var_pos) = expr { #[cfg(feature = "debugging")] self.run_debugger(scope, global, state, lib, this_ptr, expr, level)?; #[cfg(not(feature = "unchecked"))] self.inc_operations(&mut global.num_operations, expr.position())?; - return if index.is_none() && x.0.is_none() && x.2 == KEYWORD_THIS { + return if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS { this_ptr .as_deref() .cloned() @@ -396,8 +398,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] let mut sizes = (0, 0, 0); - for (crate::ast::Ident { name, .. }, value_expr) in x.0.iter() { - let key = name.as_str(); + for (key, value_expr) in x.0.iter() { let value = match self .eval_expr(scope, global, state, lib, this_ptr, value_expr, level) { @@ -411,7 +412,7 @@ impl Engine { #[cfg(not(feature = "unchecked"))] let delta = Self::calc_data_sizes(&value, true); - *map.get_mut(key).unwrap() = value; + *map.get_mut(key.as_str()).unwrap() = value; #[cfg(not(feature = "unchecked"))] if self.has_data_size_limit() { diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index a0861c88..46a1b29c 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -569,7 +569,7 @@ impl Engine { // For loop Stmt::For(x, ..) => { - let (Ident { name: var_name, .. }, counter, expr, statements) = x.as_ref(); + let (var_name, counter, expr, statements) = x.as_ref(); let iter_result = self .eval_expr(scope, global, state, lib, this_ptr, expr, level) @@ -600,14 +600,14 @@ impl Engine { if let Some(func) = func { // Add the loop variables let orig_scope_len = scope.len(); - let counter_index = if let Some(counter) = counter { + let counter_index = if !counter.is_empty() { scope.push(counter.name.clone(), 0 as INT); scope.len() - 1 } else { usize::MAX }; - scope.push(var_name.clone(), ()); + scope.push(var_name.name.clone(), ()); let index = scope.len() - 1; let mut loop_result = Ok(Dynamic::UNIT); @@ -619,7 +619,7 @@ impl Engine { if x > INT::MAX as usize { loop_result = Err(ERR::ErrorArithmetic( format!("for-loop counter overflow: {}", x), - counter.as_ref().expect("`Some`").pos, + counter.pos, ) .into()); break; @@ -707,7 +707,10 @@ impl Engine { Stmt::TryCatch(x, ..) => { let TryCatchBlock { try_block, - catch_var, + catch_var: + Ident { + name: catch_var, .. + }, catch_block, } = x.as_ref(); @@ -757,9 +760,9 @@ impl Engine { let orig_scope_len = scope.len(); - catch_var - .as_ref() - .map(|Ident { name, .. }| scope.push(name.clone(), err_value)); + if !catch_var.is_empty() { + scope.push(catch_var.clone(), err_value); + } let result = self.eval_stmt_block( scope, @@ -808,12 +811,12 @@ impl Engine { Stmt::Return(None, .., pos) => Err(ERR::Return(Dynamic::UNIT, *pos).into()), // Let/const statement - shadowing disallowed - Stmt::Var(x, .., pos) if !self.allow_shadowing() && scope.contains(&x.0.name) => { - Err(ERR::ErrorVariableExists(x.0.name.to_string(), *pos).into()) + Stmt::Var(x, .., pos) if !self.allow_shadowing() && scope.contains(&x.0) => { + Err(ERR::ErrorVariableExists(x.0.to_string(), *pos).into()) } // Let/const statement Stmt::Var(x, options, pos) => { - let (Ident { name: var_name, .. }, expr, index) = x.as_ref(); + let (var_name, expr, index) = x.as_ref(); let access = if options.contains(ASTFlags::CONSTANT) { AccessMode::ReadOnly @@ -879,7 +882,7 @@ impl Engine { )); } crate::func::locked_write(global.constants.as_ref().unwrap()) - .insert(var_name.clone(), value.clone()); + .insert(var_name.name.clone(), value.clone()); } if export { @@ -897,12 +900,12 @@ impl Engine { value.set_access_mode(access); *scope.get_mut_by_index(scope.len() - index.get()) = value; } else { - scope.push_entry(var_name.clone(), access, value); + scope.push_entry(var_name.name.clone(), access, value); } #[cfg(not(feature = "no_module"))] if let Some(alias) = _alias { - scope.add_entry_alias(scope.len() - 1, alias.clone()); + scope.add_entry_alias(scope.len() - 1, alias.name.clone()); } Ok(Dynamic::UNIT) @@ -958,14 +961,14 @@ impl Engine { }); if let Ok(module) = module_result { - if let Some(name) = export.as_ref().map(|x| x.name.clone()) { + if !export.is_empty() { if !module.is_indexed() { // Index the module (making a clone copy if necessary) if it is not indexed - let mut module = crate::func::native::shared_take_or_clone(module); - module.build_index(); - global.push_import(name, module); + let mut m = crate::func::native::shared_take_or_clone(module); + m.build_index(); + global.push_import(export.name.clone(), m); } else { - global.push_import(name, module); + global.push_import(export.name.clone(), module); } } @@ -983,7 +986,7 @@ impl Engine { // Export statement #[cfg(not(feature = "no_module"))] Stmt::Export(x, ..) => { - let (Ident { name, pos, .. }, Ident { name: alias, .. }) = x.as_ref(); + let (Ident { name, pos, .. }, alias) = x.as_ref(); // Mark scope variables as public if let Some((index, ..)) = scope.get_index(name) { scope.add_entry_alias( diff --git a/src/func/call.rs b/src/func/call.rs index 37083454..2dc1b952 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -151,14 +151,14 @@ impl Engine { #[must_use] fn gen_call_signature( &self, - #[cfg(not(feature = "no_module"))] namespace: Option<&crate::module::Namespace>, + #[cfg(not(feature = "no_module"))] namespace: &crate::ast::Namespace, fn_name: &str, args: &[&mut Dynamic], ) -> String { #[cfg(not(feature = "no_module"))] let (ns, sep) = ( - namespace.map_or_else(|| String::new(), |ns| ns.to_string()), - if namespace.is_some() { + namespace.to_string(), + if !namespace.is_empty() { crate::tokenizer::Token::DoubleColon.literal_syntax() } else { "" @@ -576,7 +576,7 @@ impl Engine { _ => Err(ERR::ErrorFunctionNotFound( self.gen_call_signature( #[cfg(not(feature = "no_module"))] - None, + &crate::ast::Namespace::NONE, name, args, ), @@ -1277,7 +1277,7 @@ impl Engine { state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - namespace: &crate::module::Namespace, + namespace: &crate::ast::Namespace, fn_name: &str, args_expr: &[Expr], hash: u64, @@ -1406,7 +1406,7 @@ impl Engine { Some(f) => unreachable!("unknown function type: {:?}", f), None => Err(ERR::ErrorFunctionNotFound( - self.gen_call_signature(Some(namespace), fn_name, &args), + self.gen_call_signature(namespace, fn_name, &args), pos, ) .into()), diff --git a/src/lib.rs b/src/lib.rs index 0c089e60..39a4fef5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -264,6 +264,10 @@ pub use ast::{ FnCallHashes, Ident, OpAssignment, ScriptFnDef, Stmt, StmtBlock, SwitchCases, TryCatchBlock, }; +#[cfg(feature = "internals")] +#[cfg(not(feature = "no_module"))] +pub use ast::Namespace; + #[cfg(feature = "internals")] #[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_function"))] @@ -279,10 +283,6 @@ pub use eval::{EvalState, GlobalRuntimeState}; #[cfg(feature = "internals")] pub use func::call::{FnResolutionCache, FnResolutionCacheEntry}; -#[cfg(feature = "internals")] -#[cfg(not(feature = "no_module"))] -pub use module::Namespace; - /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a /// specialized [`Vec`] backed by a small, inline, fixed-size array when there are ≤ 3 items stored. /// diff --git a/src/module/mod.rs b/src/module/mod.rs index 4c04142e..3d7ac021 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -2039,14 +2039,9 @@ impl Module { } } -mod namespace; - /// Module containing all built-in [module resolvers][ModuleResolver]. #[cfg(not(feature = "no_module"))] pub mod resolvers; -#[cfg(not(feature = "no_module"))] -pub use namespace::Namespace; - #[cfg(not(feature = "no_module"))] pub use resolvers::ModuleResolver; diff --git a/src/optimizer.rs b/src/optimizer.rs index 2aeb695b..ec3d155a 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -259,7 +259,7 @@ fn optimize_stmt_block( if x.1.is_constant() { state.push_var( - x.0.name.as_str(), + x.0.as_str(), AccessMode::ReadOnly, x.1.get_literal_value(), ); @@ -267,7 +267,7 @@ fn optimize_stmt_block( } else { // Add variables into the state optimize_expr(&mut x.1, state, false); - state.push_var(x.0.name.as_str(), AccessMode::ReadWrite, None); + state.push_var(x.0.as_str(), AccessMode::ReadWrite, None); } } // Optimize the statement @@ -895,7 +895,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name == prop) + *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.as_str() == prop) .map(|(.., mut expr)| { expr.set_position(*pos); expr }) .unwrap_or_else(|| Expr::Unit(*pos)); } @@ -935,7 +935,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { // Map literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.name.as_str() == s.as_str()) + *expr = mem::take(&mut m.0).into_iter().find(|(x, ..)| x.as_str() == s.as_str()) .map(|(.., mut expr)| { expr.set_position(*pos); expr }) .unwrap_or_else(|| Expr::Unit(*pos)); } @@ -1068,7 +1068,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { } // Do not call some special keywords - Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_ref()) => { + Expr::FnCall(x, ..) if DONT_EVAL_KEYWORDS.contains(&x.name.as_str()) => { x.args.iter_mut().for_each(|a| optimize_expr(a, state, false)); } @@ -1077,7 +1077,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { if !x.is_qualified() // Non-qualified && state.optimization_level == OptimizationLevel::Simple // simple optimizations && x.args.iter().all(Expr::is_constant) // all arguments are constants - //&& !is_valid_identifier(x.name.chars()) // cannot be scripted + //&& !is_valid_identifier(x.chars()) // cannot be scripted => { let arg_values = &mut x.args.iter().map(|e| e.get_literal_value().unwrap()).collect::>(); let arg_types: StaticVec<_> = arg_values.iter().map(Dynamic::type_id).collect(); @@ -1096,14 +1096,14 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { } // Overloaded operators can override built-in. _ if x.args.len() == 2 && !has_native_fn_override(state.engine, x.hashes.native, arg_types.as_ref()) => { - if let Some(result) = get_builtin_binary_op_fn(x.name.as_ref(), &arg_values[0], &arg_values[1]) + if let Some(result) = get_builtin_binary_op_fn(&x.name, &arg_values[0], &arg_values[1]) .and_then(|f| { #[cfg(not(feature = "no_function"))] let lib = state.lib; #[cfg(feature = "no_function")] let lib = &[]; - let context = (state.engine, x.name.as_str(), lib).into(); + let context = (state.engine, &x.name, lib).into(); let (first, second) = arg_values.split_first_mut().unwrap(); (f)(context, &mut [ first, &mut second[0] ]).ok() }) { @@ -1123,10 +1123,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { Expr::DynamicConstant(..) | Expr::Unit(..) | Expr::StringConstant(..) | Expr::CharConstant(..) | Expr::BoolConstant(..) | Expr::IntegerConstant(..) => (), - + #[cfg(not(feature = "no_float"))] Expr:: FloatConstant(..) => (), - + _ => if let Some(value) = arg.get_literal_value() { state.set_dirty(); *arg = Expr::DynamicConstant(value.into(), arg.start_position()); @@ -1189,10 +1189,10 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { // constant-name #[cfg(not(feature = "no_module"))] - Expr::Variable(.., x) if x.1.is_some() => (), - Expr::Variable(.., pos, x) if state.find_constant(&x.2).is_some() => { + Expr::Variable(x, ..) if !x.1.is_empty() => (), + Expr::Variable(x, .., pos) if state.find_constant(&x.3).is_some() => { // Replace constant with value - *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos); + *expr = Expr::from_dynamic(state.find_constant(&x.3).unwrap().clone(), *pos); state.set_dirty(); } diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index ce28bf37..01cb231f 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -92,7 +92,7 @@ fn collect_fn_metadata( // Create a metadata record for a function. fn make_metadata( dict: &BTreeSet, - #[cfg(not(feature = "no_module"))] namespace: Option, + #[cfg(not(feature = "no_module"))] namespace: Identifier, func: &ScriptFnDef, ) -> Map { const DICT: &str = "key exists"; @@ -100,8 +100,8 @@ fn collect_fn_metadata( let mut map = Map::new(); #[cfg(not(feature = "no_module"))] - if let Some(ns) = namespace { - map.insert(dict.get("namespace").expect(DICT).clone(), ns.into()); + if !namespace.is_empty() { + map.insert(dict.get("namespace").expect(DICT).clone(), namespace.into()); } map.insert( dict.get("name").expect(DICT).clone(), @@ -157,7 +157,7 @@ fn collect_fn_metadata( make_metadata( &dict, #[cfg(not(feature = "no_module"))] - None, + Identifier::new_const(), f, ) .into(), @@ -174,7 +174,7 @@ fn collect_fn_metadata( make_metadata( &dict, #[cfg(not(feature = "no_module"))] - None, + Identifier::new_const(), f, ) .into(), @@ -192,7 +192,7 @@ fn collect_fn_metadata( make_metadata( &dict, #[cfg(not(feature = "no_module"))] - None, + Identifier::new_const(), f, ) .into(), @@ -219,9 +219,7 @@ fn collect_fn_metadata( module .iter_script_fn() .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) - .for_each(|(.., f)| { - list.push(make_metadata(dict, Some(namespace.clone()), f).into()) - }); + .for_each(|(.., f)| list.push(make_metadata(dict, namespace.clone(), f).into())); for (ns, m) in module.iter_sub_modules() { let ns = format!( "{}{}{}", diff --git a/src/parser.rs b/src/parser.rs index 2a8949d5..6e8f6c93 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -135,7 +135,7 @@ impl<'e> ParseState<'e> { #[cfg(not(feature = "no_closure"))] if self.allow_capture { - if index == 0 && !self.external_vars.iter().any(|v| v.name == name) { + if index == 0 && !self.external_vars.iter().any(|v| v.as_str() == name) { self.external_vars.push(crate::ast::Ident { name: name.into(), pos: _pos, @@ -266,9 +266,9 @@ impl Expr { fn into_property(self, state: &mut ParseState) -> Self { match self { #[cfg(not(feature = "no_module"))] - Self::Variable(.., x) if x.1.is_some() => unreachable!("qualified property"), - Self::Variable(.., pos, x) => { - let ident = x.2; + Self::Variable(x, ..) if !x.1.is_empty() => unreachable!("qualified property"), + Self::Variable(x, .., pos) => { + let ident = x.3; let getter = state.get_identifier(crate::engine::FN_GET, &ident); let hash_get = calc_fn_hash(&getter, 1); let setter = state.get_identifier(crate::engine::FN_SET, &ident); @@ -456,7 +456,7 @@ impl Engine { lib: &mut FnLib, id: Identifier, capture_parent_scope: bool, - #[cfg(not(feature = "no_module"))] namespace: Option, + #[cfg(not(feature = "no_module"))] namespace: crate::ast::Namespace, settings: ParseSettings, ) -> ParseResult { #[cfg(not(feature = "unchecked"))] @@ -484,7 +484,7 @@ impl Engine { eat_token(input, Token::RightParen); #[cfg(not(feature = "no_module"))] - let hash = if let Some(namespace) = namespace.as_mut() { + let hash = if !namespace.is_empty() { let index = state.find_module(namespace.root()); #[cfg(not(feature = "no_function"))] @@ -499,7 +499,7 @@ impl Engine { namespace.set_index(index); - crate::calc_qualified_fn_hash(namespace.iter().map(|m| m.name.as_str()), &id, 0) + crate::calc_qualified_fn_hash(namespace.iter().map(|m| m.as_str()), &id, 0) } else { calc_fn_hash(&id, 0) }; @@ -544,7 +544,7 @@ impl Engine { eat_token(input, Token::RightParen); #[cfg(not(feature = "no_module"))] - let hash = if let Some(namespace) = namespace.as_mut() { + let hash = if !namespace.is_empty() { let index = state.find_module(namespace.root()); #[cfg(not(feature = "no_function"))] @@ -560,7 +560,7 @@ impl Engine { namespace.set_index(index); crate::calc_qualified_fn_hash( - namespace.iter().map(|m| m.name.as_str()), + namespace.iter().map(|m| m.as_str()), &id, args.len(), ) @@ -900,7 +900,7 @@ impl Engine { let (name, pos) = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), pos) | (Token::StringConstant(s), pos) => { - if map.iter().any(|(p, ..)| p.name == &*s) { + if map.iter().any(|(p, ..)| **p == s) { return Err(PERR::DuplicatedProperty(s.to_string()).into_err(pos)); } (s, pos) @@ -1387,9 +1387,9 @@ impl Engine { // Identifier Token::Identifier(..) => { #[cfg(not(feature = "no_module"))] - let none = None; + let ns = crate::ast::Namespace::NONE; #[cfg(feature = "no_module")] - let none = (); + let ns = (); let s = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), ..) => s, @@ -1405,9 +1405,9 @@ impl Engine { state.allow_capture = true; } Expr::Variable( + (None, ns, 0, state.get_identifier("", s)).into(), None, settings.pos, - (None, none, state.get_identifier("", s)).into(), ) } // Namespace qualification @@ -1419,9 +1419,9 @@ impl Engine { state.allow_capture = true; } Expr::Variable( + (None, ns, 0, state.get_identifier("", s)).into(), None, settings.pos, - (None, none, state.get_identifier("", s)).into(), ) } // Normal variable access @@ -1442,9 +1442,9 @@ impl Engine { } }); Expr::Variable( + (index, ns, 0, state.get_identifier("", s)).into(), short_index, settings.pos, - (index, none, state.get_identifier("", s)).into(), ) } } @@ -1453,9 +1453,9 @@ impl Engine { // Reserved keyword or symbol Token::Reserved(..) => { #[cfg(not(feature = "no_module"))] - let none = None; + let ns = crate::ast::Namespace::NONE; #[cfg(feature = "no_module")] - let none = (); + let ns = (); let s = match input.next().expect(NEVER_ENDS) { (Token::Reserved(s), ..) => s, @@ -1465,16 +1465,16 @@ impl Engine { match input.peek().expect(NEVER_ENDS).0 { // Function call is allowed to have reserved keyword Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( + (None, ns, 0, state.get_identifier("", s)).into(), None, settings.pos, - (None, none, state.get_identifier("", s)).into(), ), // Access to `this` as a variable is OK within a function scope #[cfg(not(feature = "no_function"))] _ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable( + (None, ns, 0, state.get_identifier("", s)).into(), None, settings.pos, - (None, none, state.get_identifier("", s)).into(), ), // Cannot access to `this` as a variable not in a function scope _ if &*s == KEYWORD_THIS => { @@ -1527,7 +1527,7 @@ impl Engine { lhs = match (lhs, tail_token) { // Qualified function call with ! #[cfg(not(feature = "no_module"))] - (Expr::Variable(.., x), Token::Bang) if x.1.is_some() => { + (Expr::Variable(x, ..), Token::Bang) if !x.1.is_empty() => { return if !match_token(input, Token::LeftParen).0 { Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string()) .into_err(tail_pos)) @@ -1540,7 +1540,7 @@ impl Engine { }; } // Function call with ! - (Expr::Variable(.., pos, x), Token::Bang) => { + (Expr::Variable(x, .., pos), Token::Bang) => { match match_token(input, Token::LeftParen) { (false, pos) => { return Err(PERR::MissingToken( @@ -1552,10 +1552,8 @@ impl Engine { _ => (), } - let (.., _ns, name) = *x; + let (.., _ns, _, name) = *x; settings.pos = pos; - #[cfg(not(feature = "no_module"))] - let _ns = _ns.map(|(ns, ..)| ns); self.parse_fn_call( input, state, @@ -1568,10 +1566,8 @@ impl Engine { )? } // Function call - (Expr::Variable(.., pos, x), Token::LeftParen) => { - let (.., _ns, name) = *x; - #[cfg(not(feature = "no_module"))] - let _ns = _ns.map(|(ns, ..)| ns); + (Expr::Variable(x, .., pos), Token::LeftParen) => { + let (.., _ns, _, name) = *x; settings.pos = pos; self.parse_fn_call( input, @@ -1586,23 +1582,17 @@ impl Engine { } // module access #[cfg(not(feature = "no_module"))] - (Expr::Variable(.., pos, x), Token::DoubleColon) => { + (Expr::Variable(x, .., pos), Token::DoubleColon) => { let (id2, pos2) = parse_var_name(input)?; - let (.., mut namespace, name) = *x; + let (.., mut namespace, _, name) = *x; let var_name_def = Ident { name, pos }; - if let Some((ref mut namespace, ..)) = namespace { - namespace.push(var_name_def); - } else { - let mut ns = crate::module::Namespace::new(); - ns.push(var_name_def); - namespace = Some((ns, 42)); - } + namespace.push(var_name_def); Expr::Variable( + (None, namespace, 0, state.get_identifier("", id2)).into(), None, pos2, - (None, namespace, state.get_identifier("", id2)).into(), ) } // Indexing @@ -1641,33 +1631,35 @@ impl Engine { // Cache the hash key for namespace-qualified variables #[cfg(not(feature = "no_module"))] let namespaced_variable = match lhs { - Expr::Variable(.., ref mut x) if x.1.is_some() => Some(x.as_mut()), + Expr::Variable(ref mut x, ..) if !x.1.is_empty() => Some(x.as_mut()), Expr::Index(ref mut x, ..) | Expr::Dot(ref mut x, ..) => match x.lhs { - Expr::Variable(.., ref mut x) if x.1.is_some() => Some(x.as_mut()), + Expr::Variable(ref mut x, ..) if !x.1.is_empty() => Some(x.as_mut()), _ => None, }, _ => None, }; #[cfg(not(feature = "no_module"))] - if let Some((.., Some((namespace, hash)), name)) = namespaced_variable { - *hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); + if let Some((.., namespace, hash, name)) = namespaced_variable { + if !namespace.is_empty() { + *hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.as_str()), name); - #[cfg(not(feature = "no_module"))] - { - let index = state.find_module(namespace.root()); + #[cfg(not(feature = "no_module"))] + { + let index = state.find_module(namespace.root()); - #[cfg(not(feature = "no_function"))] - let relax = settings.is_function_scope; - #[cfg(feature = "no_function")] - let relax = false; + #[cfg(not(feature = "no_function"))] + let relax = settings.is_function_scope; + #[cfg(feature = "no_function")] + let relax = false; - if !relax && settings.options.strict_var && index.is_none() { - return Err(PERR::ModuleUndefined(namespace.root().to_string()) - .into_err(namespace.position())); + if !relax && settings.options.strict_var && index.is_none() { + return Err(PERR::ModuleUndefined(namespace.root().to_string()) + .into_err(namespace.position())); + } + + namespace.set_index(index); } - - namespace.set_index(index); } } @@ -1826,13 +1818,13 @@ impl Engine { Err(PERR::AssignmentToConstant("".into()).into_err(lhs.start_position())) } // var (non-indexed) = rhs - Expr::Variable(None, _, ref x) if x.0.is_none() => Ok(Stmt::Assignment( + Expr::Variable(ref x, None, _) if x.0.is_none() => Ok(Stmt::Assignment( (op_info, (lhs, rhs).into()).into(), op_pos, )), // var (indexed) = rhs - Expr::Variable(i, var_pos, ref x) => { - let (index, _, name) = x.as_ref(); + Expr::Variable(ref x, i, var_pos) => { + let (index, .., name) = x.as_ref(); let index = i.map_or_else( || index.expect("either long or short index is `None`").get(), |n| n.get() as usize, @@ -1937,8 +1929,8 @@ impl Engine { } // lhs.module::id - syntax error #[cfg(not(feature = "no_module"))] - (.., Expr::Variable(.., x)) if x.1.is_some() => { - Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0.position())) + (.., Expr::Variable(x, ..)) if !x.1.is_empty() => { + Err(PERR::PropertyExpected.into_err(x.1.position())) } // lhs.id (lhs, var_expr @ Expr::Variable(..)) => { @@ -1958,7 +1950,7 @@ impl Engine { // lhs.nnn::func(...) - syntax error #[cfg(not(feature = "no_module"))] (.., Expr::FnCall(func, ..)) if func.is_qualified() => { - Err(PERR::PropertyExpected.into_err(func.namespace.expect("`Some`").position())) + Err(PERR::PropertyExpected.into_err(func.namespace.position())) } // lhs.Fn() or lhs.eval() (.., Expr::FnCall(func, func_pos)) @@ -2006,14 +1998,13 @@ 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_some() => { - Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0.position())) + Expr::Variable(x, ..) if !x.1.is_empty() => { + Err(PERR::PropertyExpected.into_err(x.1.position())) } // lhs.module::func().dot_rhs or lhs.module::func()[idx_rhs] - syntax error #[cfg(not(feature = "no_module"))] Expr::FnCall(func, ..) if func.is_qualified() => { - Err(PERR::PropertyExpected - .into_err(func.namespace.expect("`Some`").position())) + Err(PERR::PropertyExpected.into_err(func.namespace.position())) } // lhs.id.dot_rhs or lhs.id[idx_rhs] Expr::Variable(..) | Expr::Property(..) => { @@ -2294,21 +2285,15 @@ impl Engine { CUSTOM_SYNTAX_MARKER_IDENT => { let (name, pos) = parse_var_name(input)?; let name = state.get_identifier("", name); + + #[cfg(not(feature = "no_module"))] + let ns = crate::ast::Namespace::NONE; + #[cfg(feature = "no_module")] + let ns = (); + segments.push(name.clone().into()); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT)); - inputs.push(Expr::Variable( - None, - pos, - ( - None, - #[cfg(not(feature = "no_module"))] - None, - #[cfg(feature = "no_module")] - (), - name, - ) - .into(), - )); + inputs.push(Expr::Variable((None, ns, 0, name).into(), None, pos)); } CUSTOM_SYNTAX_MARKER_SYMBOL => { let (symbol, pos) = parse_symbol(input)?; @@ -2611,11 +2596,11 @@ impl Engine { ) .into_err(pos)); } - (name, name_pos, Some(counter_name), Some(counter_pos)) + (name, name_pos, counter_name, counter_pos) } else { // name let (name, name_pos) = parse_var_name(input)?; - (name, name_pos, None, None) + (name, name_pos, Identifier::new_const(), Position::NONE) }; // for name in ... @@ -2639,12 +2624,13 @@ impl Engine { let prev_stack_len = state.stack.len(); - let counter_var = counter_name.map(|name| { - let name = state.get_identifier("", name); - let pos = counter_pos.expect("`Some`"); + if !counter_name.is_empty() { state.stack.push(name.clone(), ()); - Ident { name, pos } - }); + } + let counter_var = Ident { + name: state.get_identifier("", counter_name), + pos: counter_pos, + }; let loop_var = state.get_identifier("", name); state.stack.push(loop_var.clone(), ()); @@ -2786,7 +2772,7 @@ impl Engine { // import expr as ... if !match_token(input, Token::As).0 { - return Ok(Stmt::Import((expr, None).into(), settings.pos)); + return Ok(Stmt::Import((expr, Ident::EMPTY).into(), settings.pos)); } // import expr as name ... @@ -2795,7 +2781,7 @@ impl Engine { state.imports.push(name.clone()); Ok(Stmt::Import( - (expr, Some(Ident { name, pos })).into(), + (expr, Ident { name, pos }).into(), settings.pos, )) } @@ -3229,15 +3215,15 @@ impl Engine { let name = state.get_identifier("", name); state.stack.push(name.clone(), ()); - Some(Ident { name, pos }) + Ident { name, pos } } else { - None + Ident::EMPTY }; // try { try_block } catch ( var ) { catch_block } let catch_block = self.parse_block(input, state, lib, settings.level_up())?; - if catch_var.is_some() { + if !catch_var.is_empty() { // Remove the error variable from the stack state.stack.rewind(state.stack.len() - 1); } @@ -3382,19 +3368,12 @@ impl Engine { .iter() .cloned() .map(|crate::ast::Ident { name, pos }| { - Expr::Variable( - None, - pos, - ( - None, - #[cfg(not(feature = "no_module"))] - None, - #[cfg(feature = "no_module")] - (), - name, - ) - .into(), - ) + #[cfg(not(feature = "no_module"))] + let ns = crate::ast::Namespace::NONE; + #[cfg(feature = "no_module")] + let ns = (); + + Expr::Variable((None, ns, 0, name).into(), None, pos) }), ); From 165fbbc855b2a1e7a5cdf3a6aa4137bed8c7feeb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 5 Mar 2022 18:32:50 +0800 Subject: [PATCH 3/8] Fix no_module builds. --- src/ast/expr.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/ast/expr.rs b/src/ast/expr.rs index df17cc59..52a11a29 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -380,8 +380,8 @@ pub enum Expr { Variable( #[cfg(not(feature = "no_module"))] Box<(Option, super::Namespace, u64, Identifier)>, + #[cfg(feature = "no_module")] Box<(Option, (), u64, Identifier)>, Option, - #[cfg(feature = "no_module")] Box<(Option, (), Identifier)>, Position, ), /// Property access - ((getter, hash), (setter, hash), prop) From b35d965e552ae739417d5e03624180ff9dea0184 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 6 Mar 2022 16:37:27 +0800 Subject: [PATCH 4/8] Reverse Box<[...]> to Vec. --- CHANGELOG.md | 5 ----- src/ast/script_fn.rs | 23 +++++------------------ src/eval/chaining.rs | 12 +++++++----- src/module/mod.rs | 10 +++++----- src/optimizer.rs | 2 +- src/parser.rs | 18 ++++++------------ src/serde/metadata.rs | 10 ++++------ 7 files changed, 28 insertions(+), 52 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bc700e7c..da27dcba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,11 +17,6 @@ Bug fixes * Exporting a variable that contains a local function pointer (including anonymous function or closure) now raises a runtime error. * Full optimization is now skipped for method calls. -Breaking changes ----------------- - -* `ScriptFnMetadata` fields `params` and `comments` are changed to boxed slices instead of `Vec`. In the vast majority of cases this should not cause code breakage. - Enhancements ------------ diff --git a/src/ast/script_fn.rs b/src/ast/script_fn.rs index 3d810492..2ebbc9ed 100644 --- a/src/ast/script_fn.rs +++ b/src/ast/script_fn.rs @@ -46,7 +46,7 @@ pub struct ScriptFnDef { /// _(metadata)_ Function doc-comments (if any). /// Exported under the `metadata` feature only. #[cfg(feature = "metadata")] - pub comments: Option]>>, + pub comments: Box<[Box]>, } impl fmt::Display for ScriptFnDef { @@ -79,7 +79,7 @@ pub struct ScriptFnMetadata<'a> { /// Function name. pub name: &'a str, /// Function parameters (if any). - pub params: Box<[&'a str]>, + pub params: Vec<&'a str>, /// Function access mode. pub access: FnAccess, /// _(metadata)_ Function doc-comments (if any). @@ -93,7 +93,7 @@ pub struct ScriptFnMetadata<'a> { /// corresponding doc-comment leader: `///` or `/**`. /// Function access mode. #[cfg(feature = "metadata")] - pub comments: Box<[&'a str]>, + pub comments: Vec<&'a str>, } impl fmt::Display for ScriptFnMetadata<'_> { @@ -120,23 +120,10 @@ impl<'a> From<&'a ScriptFnDef> for ScriptFnMetadata<'a> { fn from(value: &'a ScriptFnDef) -> Self { Self { name: &value.name, - params: value - .params - .iter() - .map(|s| s.as_str()) - .collect::>() - .into_boxed_slice(), + params: value.params.iter().map(|s| s.as_str()).collect(), access: value.access, #[cfg(feature = "metadata")] - comments: value.comments.as_ref().map_or_else( - || Vec::new().into_boxed_slice(), - |v| { - v.iter() - .map(Box::as_ref) - .collect::>() - .into_boxed_slice() - }, - ), + comments: value.comments.iter().map(Box::as_ref).collect(), } } } diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index 3f9718f2..ad055595 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -45,7 +45,7 @@ pub enum ChainArgument { /// Since many dotted function calls have no arguments (e.g. `string.len()`), it is better to /// reduce the size of [`ChainArgument`] by using a boxed slice. #[cfg(not(feature = "no_object"))] - MethodCallArgs(Option>, Position), + MethodCallArgs(Box<[Dynamic]>, Position), /// Index value and [position][Position]. #[cfg(not(feature = "no_index"))] IndexValue(Dynamic, Position), @@ -73,8 +73,10 @@ impl ChainArgument { #[must_use] pub fn into_fn_call_args(self) -> (crate::FnArgsVec, Position) { match self { - Self::MethodCallArgs(None, pos) => (crate::FnArgsVec::new_const(), pos), - Self::MethodCallArgs(Some(mut values), pos) => { + Self::MethodCallArgs(values, pos) if values.is_empty() => { + (crate::FnArgsVec::new_const(), pos) + } + Self::MethodCallArgs(mut values, pos) => { (values.iter_mut().map(std::mem::take).collect(), pos) } x => unreachable!("ChainArgument::MethodCallArgs expected but gets {:?}", x), @@ -100,9 +102,9 @@ impl ChainArgument { #[must_use] pub fn from_fn_call_args(values: crate::FnArgsVec, pos: Position) -> Self { if values.is_empty() { - Self::MethodCallArgs(None, pos) + Self::MethodCallArgs(Box::default(), pos) } else { - Self::MethodCallArgs(Some(values.into_vec().into()), pos) + Self::MethodCallArgs(values.into_boxed_slice(), pos) } } /// Create an [`IndexValue`][ChainArgument::IndexValue]. diff --git a/src/module/mod.rs b/src/module/mod.rs index 3d7ac021..0491e3e9 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -51,7 +51,7 @@ pub struct FnMetadata { pub return_type: Identifier, /// Comments. #[cfg(feature = "metadata")] - pub comments: Option]>>, + pub comments: Box<[Box]>, } impl PartialOrd for FnMetadata { @@ -597,7 +597,7 @@ impl Module { #[cfg(feature = "metadata")] return_type: "".into(), #[cfg(feature = "metadata")] - comments: None, + comments: Box::default(), }, func: Into::::into(fn_def).into(), param_types: StaticVec::new_const(), @@ -816,7 +816,7 @@ impl Module { if !comments.is_empty() { let f = self.functions.get_mut(&hash_fn).unwrap(); - f.metadata.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); + f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect(); } self @@ -923,7 +923,7 @@ impl Module { #[cfg(feature = "metadata")] return_type: return_type_name, #[cfg(feature = "metadata")] - comments: None, + comments: Box::default(), }, func: func.into(), param_types, @@ -981,7 +981,7 @@ impl Module { if !comments.is_empty() { let f = self.functions.get_mut(&hash).unwrap(); - f.metadata.comments = Some(comments.iter().map(|s| s.as_ref().into()).collect()); + f.metadata.comments = comments.iter().map(|s| s.as_ref().into()).collect(); } hash diff --git a/src/optimizer.rs b/src/optimizer.rs index ec3d155a..579b8eec 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -1277,7 +1277,7 @@ pub fn optimize_into_ast( environ: None, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - comments: None, + comments: Box::default(), }); } diff --git a/src/parser.rs b/src/parser.rs index 6e8f6c93..f7e7b9b0 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -3330,17 +3330,11 @@ impl Engine { environ: None, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - comments: if comments.is_empty() { - None - } else { - Some( - comments - .into_iter() - .map(|s| s.to_string().into_boxed_str()) - .collect::>() - .into_boxed_slice(), - ) - }, + comments: comments + .into_iter() + .map(|s| s.to_string().into_boxed_str()) + .collect::>() + .into_boxed_slice(), }) } @@ -3495,7 +3489,7 @@ impl Engine { environ: None, #[cfg(not(feature = "no_function"))] #[cfg(feature = "metadata")] - comments: None, + comments: Box::default(), }; let fn_ptr = crate::FnPtr::new_unchecked(fn_name, StaticVec::new_const()); diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 42b46a91..652f5122 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -145,13 +145,11 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { .get_script_fn_def() .expect("script-defined function") .comments - .as_ref() - .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) + .iter() + .map(Box::as_ref) + .collect() } else { - info.metadata - .comments - .as_ref() - .map_or_else(|| Vec::new(), |v| v.iter().map(|s| &**s).collect()) + info.metadata.comments.iter().map(Box::as_ref).collect() }, } } From cbb1c5c6a021b7ad09d8f7c4f6c30209df244066 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 8 Mar 2022 18:26:32 +0800 Subject: [PATCH 5/8] Add sleep. --- CHANGELOG.md | 1 + src/packages/lang_core.rs | 47 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index da27dcba..eeea89c0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ Enhancements * `FnAccess` and `FnNamespace` now implement `Ord` and `PartialOrd`. * The `event_handler_map` example is enhanced to prevent shadowing of the state object map. * Separation of constants in function calls is removed as its performance benefit is dubious. +* A function `sleep` is added to block the current thread by a specified number of seconds. Version 1.5.0 diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 01cb231f..a2cd5150 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -5,6 +5,9 @@ use crate::{Dynamic, RhaiResultOf, ERR, INT}; #[cfg(feature = "no_std")] use std::prelude::v1::*; +#[cfg(not(feature = "no_float"))] +use crate::FLOAT; + def_package! { /// Package of core language features. pub LanguageCorePackage(lib) { @@ -21,10 +24,32 @@ def_package! { #[export_module] mod core_functions { + /// Return the _tag_ of a `Dynamic` value. + /// + /// # Example + /// + /// ```rhai + /// let x = "hello, world!"; + /// + /// x.tag = 42; + /// + /// print(x.tag); // prints 42 + /// ``` #[rhai_fn(name = "tag", get = "tag", pure)] pub fn get_tag(value: &mut Dynamic) -> INT { value.tag() as INT } + /// Set the _tag_ of a `Dynamic` value. + /// + /// # Example + /// + /// ```rhai + /// let x = "hello, world!"; + /// + /// x.tag = 42; + /// + /// print(x.tag); // prints 42 + /// ``` #[rhai_fn(name = "set_tag", set = "tag", return_raw)] pub fn set_tag(value: &mut Dynamic, tag: INT) -> RhaiResultOf<()> { if tag < Tag::MIN as INT { @@ -54,6 +79,28 @@ mod core_functions { Ok(()) } } + + /// Block the current thread for a particular number of `seconds`. + #[cfg(not(feature = "no_float"))] + #[rhai_fn(name = "float")] + pub fn sleep_float(seconds: FLOAT) { + if seconds <= 0.0 { + return; + } + + #[cfg(not(feature = "f32_float"))] + std::thread::sleep(std::time::Duration::from_secs_f64(seconds)); + #[cfg(feature = "f32_float")] + std::thread::sleep(std::time::Duration::from_secs_f32(seconds)); + } + + /// Block the current thread for a particular number of `seconds`. + pub fn sleep(seconds: INT) { + if seconds <= 0 { + return; + } + std::thread::sleep(std::time::Duration::from_secs(seconds as u64)); + } } #[cfg(not(feature = "no_function"))] From 89426f8b3a194d7cd3d3137688d8080b229d8364 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 9 Mar 2022 09:25:32 +0800 Subject: [PATCH 6/8] Add Scope::set_alias. --- CHANGELOG.md | 1 + src/eval/stmt.rs | 8 +++----- src/types/scope.rs | 26 +++++++++++++++++++++++++- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index eeea89c0..0f9eac99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -25,6 +25,7 @@ Enhancements * The `event_handler_map` example is enhanced to prevent shadowing of the state object map. * Separation of constants in function calls is removed as its performance benefit is dubious. * A function `sleep` is added to block the current thread by a specified number of seconds. +* `Scope::set_alias` is added to export a variable under a particular alias name. Version 1.5.0 diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 46a1b29c..1a1244fd 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -905,7 +905,7 @@ impl Engine { #[cfg(not(feature = "no_module"))] if let Some(alias) = _alias { - scope.add_entry_alias(scope.len() - 1, alias.name.clone()); + scope.add_alias_by_index(scope.len() - 1, alias.name.clone()); } Ok(Dynamic::UNIT) @@ -989,10 +989,8 @@ impl Engine { let (Ident { name, pos, .. }, alias) = x.as_ref(); // Mark scope variables as public if let Some((index, ..)) = scope.get_index(name) { - scope.add_entry_alias( - index, - if alias.is_empty() { name } else { alias }.clone(), - ); + let alias = if alias.is_empty() { name } else { alias }.clone(); + scope.add_alias_by_index(index, alias); Ok(Dynamic::UNIT) } else { Err(ERR::ErrorVariableNotFound(name.to_string(), *pos).into()) diff --git a/src/types/scope.rs b/src/types/scope.rs index 199940e9..bae3f2a8 100644 --- a/src/types/scope.rs +++ b/src/types/scope.rs @@ -514,13 +514,37 @@ impl Scope<'_> { /// Panics if the index is out of bounds. #[cfg(not(feature = "no_module"))] #[inline] - pub(crate) fn add_entry_alias(&mut self, index: usize, alias: Identifier) -> &mut Self { + pub(crate) fn add_alias_by_index(&mut self, index: usize, alias: Identifier) -> &mut Self { let aliases = self.aliases.get_mut(index).unwrap(); if aliases.is_empty() || !aliases.contains(&alias) { aliases.push(alias); } self } + /// Add an alias to a variable in the [`Scope`] so that it is exported under that name. + /// This is an advanced API. + /// + /// If the alias is empty, then the variable is exported under its original name. + /// + /// Multiple aliases can be added to any variable. + /// + /// Only the last variable matching the name (and not other shadowed versions) is aliased by + /// this call. + #[cfg(not(feature = "no_module"))] + #[inline] + pub fn set_alias( + &mut self, + name: impl AsRef + Into, + alias: impl Into, + ) { + if let Some((index, ..)) = self.get_index(name.as_ref()) { + let alias = match alias.into() { + x if x.is_empty() => name.into(), + x => x, + }; + self.add_alias_by_index(index, alias); + } + } /// Clone the [`Scope`], keeping only the last instances of each variable name. /// Shadowed variables are omitted in the copy. #[inline] From 1e4abd012c052a362fc2753a981e55c0ad90d3b2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 9 Mar 2022 09:25:55 +0800 Subject: [PATCH 7/8] Minor refactor. --- src/api/register.rs | 2 +- src/ast/flags.rs | 10 +++++----- src/ast/stmt.rs | 2 +- src/eval/chaining.rs | 28 ++++++++++++---------------- src/lib.rs | 16 ++++++++++++++++ src/optimizer.rs | 8 ++++---- src/packages/bit_field.rs | 33 ++++++++++++++++----------------- src/packages/blob_basic.rs | 9 ++------- src/packages/iter_basic.rs | 12 +++++------- src/packages/time_basic.rs | 10 ++++++++++ tests/blobs.rs | 30 ++++++++++++++---------------- 11 files changed, 86 insertions(+), 74 deletions(-) diff --git a/src/api/register.rs b/src/api/register.rs index acd35914..72720c7a 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -293,7 +293,7 @@ impl Engine { self } /// Register an type iterator for an iterable type with the [`Engine`]. - /// This is an advanced feature. + /// This is an advanced API. #[inline(always)] pub fn register_iterator(&mut self) -> &mut Self where diff --git a/src/ast/flags.rs b/src/ast/flags.rs index 264a6374..6d2506ba 100644 --- a/src/ast/flags.rs +++ b/src/ast/flags.rs @@ -18,14 +18,14 @@ bitflags! { /// Exported under the `internals` feature only. pub struct ASTFlags: u8 { /// No options for the [`AST`][crate::AST] node. - const NONE = 0b0000_0000; + const NONE = 0b_0000_0000; /// The [`AST`][crate::AST] node is read-only. - const CONSTANT = 0b0000_0001; + const CONSTANT = 0b_0000_0001; /// The [`AST`][crate::AST] node is exposed to the outside (i.e. public). - const EXPORTED = 0b0000_0010; + const EXPORTED = 0b_0000_0010; /// The [`AST`][crate::AST] node is negated (i.e. whatever information is the opposite). - const NEGATED = 0b0000_0100; + const NEGATED = 0b_0000_0100; /// The [`AST`][crate::AST] node breaks out of normal control flow. - const BREAK = 0b0000_1000; + const BREAK = 0b_0000_1000; } } diff --git a/src/ast/stmt.rs b/src/ast/stmt.rs index 9ef68e10..815c0f8b 100644 --- a/src/ast/stmt.rs +++ b/src/ast/stmt.rs @@ -621,7 +621,7 @@ impl Stmt { /// upper block. /// /// Currently only variable definitions (i.e. `let` and `const`), `import`/`export` statements, - /// and `eval` calls (which may in turn call define variables) fall under this category. + /// and `eval` calls (which may in turn define variables) fall under this category. #[inline] #[must_use] pub fn is_block_dependent(&self) -> bool { diff --git a/src/eval/chaining.rs b/src/eval/chaining.rs index ad055595..3cb2076a 100644 --- a/src/eval/chaining.rs +++ b/src/eval/chaining.rs @@ -922,22 +922,20 @@ impl Engine { if idx.is::() || idx.is::() => { // val_int[range] - const BITS: usize = std::mem::size_of::() * 8; - let (shift, mask) = if let Some(range) = idx.read_lock::() { let start = range.start; let end = range.end; - let start = super::calc_index(BITS, start, false, || { - ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into() + let start = super::calc_index(crate::INT_BITS, start, false, || { + ERR::ErrorBitFieldBounds(crate::INT_BITS, start, idx_pos).into() })?; - let end = super::calc_index(BITS, end, false, || { - ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into() + let end = super::calc_index(crate::INT_BITS, end, false, || { + ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into() })?; if end <= start { (0, 0) - } else if end == BITS && start == 0 { + } else if end == crate::INT_BITS && start == 0 { // -1 = all bits set (0, -1) } else { @@ -953,16 +951,16 @@ impl Engine { let start = *range.start(); let end = *range.end(); - let start = super::calc_index(BITS, start, false, || { - ERR::ErrorBitFieldBounds(BITS, start, idx_pos).into() + let start = super::calc_index(crate::INT_BITS, start, false, || { + ERR::ErrorBitFieldBounds(crate::INT_BITS, start, idx_pos).into() })?; - let end = super::calc_index(BITS, end, false, || { - ERR::ErrorBitFieldBounds(BITS, end, idx_pos).into() + let end = super::calc_index(crate::INT_BITS, end, false, || { + ERR::ErrorBitFieldBounds(crate::INT_BITS, end, idx_pos).into() })?; if end < start { (0, 0) - } else if end == BITS - 1 && start == 0 { + } else if end == crate::INT_BITS - 1 && start == 0 { // -1 = all bits set (0, -1) } else { @@ -995,10 +993,8 @@ impl Engine { .as_int() .map_err(|typ| self.make_type_mismatch_err::(typ, idx_pos))?; - const BITS: usize = std::mem::size_of::() * 8; - - let bit = super::calc_index(BITS, index, true, || { - ERR::ErrorBitFieldBounds(BITS, index, idx_pos).into() + let bit = super::calc_index(crate::INT_BITS, index, true, || { + ERR::ErrorBitFieldBounds(crate::INT_BITS, index, idx_pos).into() })?; let bit_value = (*value & (1 << bit)) != 0; diff --git a/src/lib.rs b/src/lib.rs index 39a4fef5..5e6c444a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -121,6 +121,16 @@ type UNSIGNED_INT = u64; #[allow(non_camel_case_types)] type UNSIGNED_INT = u32; +/// Number of bits in [`INT`]. +/// +/// It is 64 unless the `only_i32` feature is enabled when it will be 32. +const INT_BITS: usize = std::mem::size_of::() * 8; + +/// Number of bytes that make up an [`INT`]. +/// +/// It is 8 unless the `only_i32` feature is enabled when it will be 4. +const INT_BYTES: usize = std::mem::size_of::(); + /// The system floating-point type. It is defined as [`f64`]. /// /// Not available under `no_float`. @@ -140,6 +150,12 @@ pub type FLOAT = f64; #[cfg(feature = "f32_float")] pub type FLOAT = f32; +/// Number of bytes that make up a [`FLOAT`]. +/// +/// It is 8 unless the `f32_float` feature is enabled when it will be 4. +#[cfg(not(feature = "no_float"))] +const FLOAT_BYTES: usize = std::mem::size_of::(); + /// An exclusive integer range. type ExclusiveRange = std::ops::Range; diff --git a/src/optimizer.rs b/src/optimizer.rs index 579b8eec..a8fa2e4c 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -10,7 +10,7 @@ use crate::tokenizer::{Span, Token}; use crate::types::dynamic::AccessMode; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, Position, Scope, - StaticVec, AST, INT, + StaticVec, AST, INT, INT_BITS, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -940,16 +940,16 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { .unwrap_or_else(|| Expr::Unit(*pos)); } // int[int] - (Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < (std::mem::size_of_val(n) * 8) => { + (Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < INT_BITS => { // Bit-field literal indexing - get the bit state.set_dirty(); *expr = Expr::BoolConstant((*n & (1 << (*i as usize))) != 0, *pos); } // int[-int] - (Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|i| i as usize <= (std::mem::size_of_val(n) * 8)).unwrap_or(false) => { + (Expr::IntegerConstant(n, pos), Expr::IntegerConstant(i, ..)) if *i < 0 && i.checked_abs().map(|i| i as usize <= INT_BITS).unwrap_or(false) => { // Bit-field literal indexing - get the bit state.set_dirty(); - *expr = Expr::BoolConstant((*n & (1 << (std::mem::size_of_val(n) * 8 - i.abs() as usize))) != 0, *pos); + *expr = Expr::BoolConstant((*n & (1 << (INT_BITS - i.abs() as usize))) != 0, *pos); } // string[int] (Expr::StringConstant(s, pos), Expr::IntegerConstant(i, ..)) if *i >= 0 && (*i as usize) < s.chars().count() => { diff --git a/src/packages/bit_field.rs b/src/packages/bit_field.rs index 55b4ad40..5deb09ce 100644 --- a/src/packages/bit_field.rs +++ b/src/packages/bit_field.rs @@ -1,7 +1,8 @@ use crate::eval::calc_index; use crate::plugin::*; use crate::{ - def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, UNSIGNED_INT, + def_package, ExclusiveRange, InclusiveRange, Position, RhaiResultOf, ERR, INT, INT_BITS, + UNSIGNED_INT, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -17,8 +18,6 @@ def_package! { #[export_module] mod bit_field_functions { - const BITS: usize = std::mem::size_of::() * 8; - /// Return `true` if the specified `bit` in the number is set. /// /// If `bit` < 0, position counts from the MSB (Most Significant Bit). @@ -36,8 +35,8 @@ mod bit_field_functions { /// ``` #[rhai_fn(return_raw)] pub fn get_bit(value: INT, bit: INT) -> RhaiResultOf { - let bit = calc_index(BITS, bit, true, || { - ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into() + let bit = calc_index(INT_BITS, bit, true, || { + ERR::ErrorBitFieldBounds(INT_BITS, bit, Position::NONE).into() })?; Ok((value & (1 << bit)) != 0) @@ -66,8 +65,8 @@ mod bit_field_functions { /// ``` #[rhai_fn(return_raw)] pub fn set_bit(value: &mut INT, bit: INT, new_value: bool) -> RhaiResultOf<()> { - let bit = calc_index(BITS, bit, true, || { - ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into() + let bit = calc_index(INT_BITS, bit, true, || { + ERR::ErrorBitFieldBounds(INT_BITS, bit, Position::NONE).into() })?; let mask = 1 << bit; @@ -128,17 +127,17 @@ mod bit_field_functions { return Ok(0); } - let bit = calc_index(BITS, start, true, || { - ERR::ErrorBitFieldBounds(BITS, start, Position::NONE).into() + let bit = calc_index(INT_BITS, start, true, || { + ERR::ErrorBitFieldBounds(INT_BITS, start, Position::NONE).into() })?; - let bits = if bit + bits as usize > BITS { - BITS - bit + let bits = if bit + bits as usize > INT_BITS { + INT_BITS - bit } else { bits as usize }; - if bit == 0 && bits == BITS { + if bit == 0 && bits == INT_BITS { return Ok(value); } @@ -214,17 +213,17 @@ mod bit_field_functions { return Ok(()); } - let bit = calc_index(BITS, bit, true, || { - ERR::ErrorBitFieldBounds(BITS, bit, Position::NONE).into() + let bit = calc_index(INT_BITS, bit, true, || { + ERR::ErrorBitFieldBounds(INT_BITS, bit, Position::NONE).into() })?; - let bits = if bit + bits as usize > BITS { - BITS - bit + let bits = if bit + bits as usize > INT_BITS { + INT_BITS - bit } else { bits as usize }; - if bit == 0 && bits == BITS { + if bit == 0 && bits == INT_BITS { *value = new_value; return Ok(()); } diff --git a/src/packages/blob_basic.rs b/src/packages/blob_basic.rs index 2a21b0d2..92a5b684 100644 --- a/src/packages/blob_basic.rs +++ b/src/packages/blob_basic.rs @@ -4,19 +4,14 @@ use crate::eval::{calc_index, calc_offset_len}; use crate::plugin::*; use crate::{ def_package, Array, Blob, Dynamic, ExclusiveRange, InclusiveRange, NativeCallContext, Position, - RhaiResultOf, INT, + RhaiResultOf, INT, INT_BYTES, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{any::TypeId, mem}; #[cfg(not(feature = "no_float"))] -use crate::FLOAT; - -const INT_BYTES: usize = mem::size_of::(); - -#[cfg(not(feature = "no_float"))] -const FLOAT_BYTES: usize = mem::size_of::(); +use crate::{FLOAT, FLOAT_BYTES}; def_package! { /// Package of basic BLOB utilities. diff --git a/src/packages/iter_basic.rs b/src/packages/iter_basic.rs index bcf50691..95ccdf7a 100644 --- a/src/packages/iter_basic.rs +++ b/src/packages/iter_basic.rs @@ -1,7 +1,7 @@ use crate::eval::calc_index; use crate::plugin::*; use crate::types::dynamic::Variant; -use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT}; +use crate::{def_package, ExclusiveRange, InclusiveRange, RhaiResultOf, INT, INT_BITS}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ @@ -117,18 +117,16 @@ impl FusedIterator for StepRange where #[derive(Debug, Clone, Copy, Hash, Eq, PartialEq)] struct BitRange(INT, INT, usize); -const BITS: usize = std::mem::size_of::() * 8; - impl BitRange { pub fn new(value: INT, from: INT, len: INT) -> RhaiResultOf { - let from = calc_index(BITS, from, true, || { - crate::ERR::ErrorBitFieldBounds(BITS, from, Position::NONE).into() + let from = calc_index(INT_BITS, from, true, || { + crate::ERR::ErrorBitFieldBounds(INT_BITS, from, Position::NONE).into() })?; let len = if len < 0 { 0 - } else if from + (len as usize) > BITS { - BITS - from + } else if from + (len as usize) > INT_BITS { + INT_BITS - from } else { len as usize }; diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index b631a59d..4f51f8fe 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -31,6 +31,16 @@ mod time_functions { } /// Return the number of seconds between the current system time and the timestamp. + /// + /// # Example + /// + /// ```rhai + /// let now = timestamp(); + /// + /// sleep(10.0); // sleep for 10 seconds + /// + /// print(now.elapsed); // prints 10.??? + /// ``` #[rhai_fn(name = "elapsed", get = "elapsed", return_raw)] pub fn elapsed(timestamp: Instant) -> RhaiResult { #[cfg(not(feature = "no_float"))] diff --git a/tests/blobs.rs b/tests/blobs.rs index 5653e1ef..d961563f 100644 --- a/tests/blobs.rs +++ b/tests/blobs.rs @@ -9,66 +9,64 @@ fn test_blobs() -> Result<(), Box> { a.push(3); let engine = Engine::new(); - let mut orig_scope = Scope::new(); - orig_scope.push("x", a); - - let mut scope = orig_scope.clone(); + let mut scope = Scope::new(); + scope.push("x", a); assert_eq!(engine.eval_with_scope::(&mut scope, "x[1]")?, 2); assert_eq!(engine.eval_with_scope::(&mut scope, "x[0]")?, 1); assert_eq!(engine.eval_with_scope::(&mut scope, "x[-1]")?, 3); assert_eq!(engine.eval_with_scope::(&mut scope, "x[-3]")?, 1); assert_eq!( - engine.eval_with_scope::(&mut scope, "x += 4; x[3]")?, + engine.eval_with_scope::(&mut scope.clone(), "x += 4; x[3]")?, 4 ); #[cfg(not(feature = "no_object"))] { assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x.push(4); x")?, + engine.eval_with_scope::(&mut scope.clone(), "x.push(4); x")?, [1, 2, 3, 4] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x.insert(0, 4); x")?, + engine.eval_with_scope::(&mut scope.clone(), "x.insert(0, 4); x")?, [4, 1, 2, 3] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x.insert(999, 4); x")?, + engine.eval_with_scope::(&mut scope.clone(), "x.insert(999, 4); x")?, [1, 2, 3, 4] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x.insert(-2, 4); x")?, + engine.eval_with_scope::(&mut scope.clone(), "x.insert(-2, 4); x")?, [1, 4, 2, 3] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x.insert(-999, 4); x")?, + engine.eval_with_scope::(&mut scope.clone(), "x.insert(-999, 4); x")?, [4, 1, 2, 3] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "let z = [42]; x[z.len]")?, + engine.eval_with_scope::(&mut scope.clone(), "let z = [42]; x[z.len]")?, 2 ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "let z = [2]; x[z[0]]")?, + engine.eval_with_scope::(&mut scope.clone(), "let z = [2]; x[z[0]]")?, 3 ); } assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x += x; x")?, + engine.eval_with_scope::(&mut scope.clone(), "x += x; x")?, [1, 2, 3, 1, 2, 3] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x + x")?, + engine.eval_with_scope::(&mut scope.clone(), "x + x")?, [1, 2, 3, 1, 2, 3] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x += 999; x")?, + engine.eval_with_scope::(&mut scope.clone(), "x += 999; x")?, [1, 2, 3, 0xe7] ); assert_eq!( - engine.eval_with_scope::(&mut orig_scope.clone(), "x[2] = 999; x")?, + engine.eval_with_scope::(&mut scope.clone(), "x[2] = 999; x")?, [1, 2, 0xe7] ); From 963af0653e2672cc5784928bfb54c7e07ab75a6a Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 9 Mar 2022 09:41:53 +0800 Subject: [PATCH 8/8] Fix sleep function. --- src/packages/lang_core.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index a2cd5150..41d91c01 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -82,7 +82,8 @@ mod core_functions { /// Block the current thread for a particular number of `seconds`. #[cfg(not(feature = "no_float"))] - #[rhai_fn(name = "float")] + #[cfg(not(feature = "no_std"))] + #[rhai_fn(name = "sleep")] pub fn sleep_float(seconds: FLOAT) { if seconds <= 0.0 { return; @@ -95,6 +96,7 @@ mod core_functions { } /// Block the current thread for a particular number of `seconds`. + #[cfg(not(feature = "no_std"))] pub fn sleep(seconds: INT) { if seconds <= 0 { return;