diff --git a/src/ast.rs b/src/ast.rs index f2e41d98..546c17ee 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -921,7 +921,7 @@ pub struct FnCallExpr { /// Default value when the function is not found, mostly used to provide a default for comparison functions. pub def_value: Option, /// Namespace of the function, if any. Boxed because it occurs rarely. - pub namespace: Option>, + pub namespace: Option, /// Function name. /// Use [`Cow<'static, str>`][Cow] because a lot of operators (e.g. `==`, `>=`) are implemented as /// function calls and the function names are predictable, so no need to allocate a new [`String`]. @@ -961,12 +961,11 @@ pub enum Expr { Map(Box>, Position), /// () Unit(Position), - /// Variable access - (optional index, optional modules, hash, variable name) + /// Variable access - (optional index, optional (hash, modules), variable name) Variable( Box<( Option, - Option>, - Option, + Option<(NonZeroU64, NamespaceRef)>, Ident, )>, ), @@ -1049,7 +1048,7 @@ impl Expr { /// Is the expression a simple variable access? pub(crate) fn get_variable_access(&self, non_qualified: bool) -> Option<&str> { match self { - Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.3).name.as_str()), + Self::Variable(x) if !non_qualified || x.1.is_none() => Some((x.2).name.as_str()), _ => None, } } @@ -1071,7 +1070,7 @@ impl Expr { Self::Map(_, pos) => *pos, Self::Property(x) => (x.1).pos, Self::Stmt(_, pos) => *pos, - Self::Variable(x) => (x.3).pos, + Self::Variable(x) => (x.2).pos, Self::FnCall(_, pos) => *pos, Self::And(x, _) | Self::Or(x, _) | Self::In(x, _) => x.lhs.position(), @@ -1101,7 +1100,7 @@ impl Expr { Self::FnPointer(_, pos) => *pos = new_pos, Self::Array(_, pos) => *pos = new_pos, Self::Map(_, pos) => *pos = new_pos, - Self::Variable(x) => (x.3).pos = new_pos, + Self::Variable(x) => (x.2).pos = new_pos, Self::Property(x) => (x.1).pos = new_pos, Self::Stmt(_, pos) => *pos = new_pos, Self::FnCall(_, pos) => *pos = new_pos, diff --git a/src/engine.rs b/src/engine.rs index fdc735ea..58e7b930 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -861,20 +861,17 @@ impl Engine { match expr { Expr::Variable(v) => match v.as_ref() { // Qualified variable - (_, Some(modules), hash_var, Ident { name, pos }) => { + (_, Some((hash_var, modules)), Ident { name, pos }) => { let module = search_imports(mods, state, modules)?; - let target = - module - .get_qualified_var(hash_var.unwrap()) - .map_err(|mut err| { - match *err { - EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { - *err_name = format!("{}{}", modules, name); - } - _ => (), - } - err.fill_position(*pos) - })?; + let target = module.get_qualified_var(*hash_var).map_err(|mut err| { + match *err { + EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { + *err_name = format!("{}{}", modules, name); + } + _ => (), + } + err.fill_position(*pos) + })?; // Module variables are constant let mut target = target.clone(); @@ -898,7 +895,7 @@ impl Engine { this_ptr: &'s mut Option<&mut Dynamic>, expr: &'a Expr, ) -> Result<(Target<'s>, &'a str, Position), Box> { - let (index, _, _, Ident { name, pos }) = match expr { + let (index, _, Ident { name, pos }) = match expr { Expr::Variable(v) => v.as_ref(), _ => unreachable!(), }; @@ -1299,7 +1296,7 @@ impl Engine { let Ident { name: var_name, pos: var_pos, - } = &x.3; + } = &x.2; self.inc_operations(state, *var_pos)?; @@ -1715,11 +1712,11 @@ impl Engine { Expr::StringConstant(x, _) => Ok(x.clone().into()), Expr::CharConstant(x, _) => Ok((*x).into()), Expr::FnPointer(x, _) => Ok(FnPtr::new_unchecked(x.clone(), Default::default()).into()), - Expr::Variable(x) if (x.3).name == KEYWORD_THIS => { + Expr::Variable(x) if (x.2).name == KEYWORD_THIS => { if let Some(val) = this_ptr { Ok(val.clone()) } else { - EvalAltResult::ErrorUnboundThis((x.3).pos).into() + EvalAltResult::ErrorUnboundThis((x.2).pos).into() } } Expr::Variable(_) => { @@ -1795,7 +1792,7 @@ impl Engine { def_value, .. } = x.as_ref(); - let namespace = namespace.as_ref().map(|v| v.as_ref()); + let namespace = namespace.as_ref(); let hash = hash.unwrap(); let def_value = def_value.as_ref(); self.make_qualified_function_call( diff --git a/src/module/mod.rs b/src/module/mod.rs index ba0b8a38..8d5c880d 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1913,7 +1913,7 @@ impl Module { /// _(INTERNALS)_ A chain of [module][Module] names to namespace-qualify a variable or function call. /// Exported under the `internals` feature only. /// -/// A [`NonZeroU64`] hash key is cached for quick search purposes. +/// A [`NonZeroU64`] offset to the current [`Scope`][crate::Scope] is cached for quick search purposes. /// /// A [`StaticVec`] is used because most namespace-qualified access contains only one level, /// and it is wasteful to always allocate a [`Vec`] with one element. @@ -1922,13 +1922,13 @@ impl Module { /// /// This type is volatile and may change. #[derive(Clone, Eq, PartialEq, Default, Hash)] -pub struct NamespaceRef(StaticVec, Option); +pub struct NamespaceRef(Option, StaticVec); impl fmt::Debug for NamespaceRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - fmt::Debug::fmt(&self.0, f)?; + fmt::Debug::fmt(&self.1, f)?; - if let Some(index) = self.1 { + if let Some(index) = self.0 { write!(f, " -> {}", index) } else { Ok(()) @@ -1940,19 +1940,19 @@ impl Deref for NamespaceRef { type Target = StaticVec; fn deref(&self) -> &Self::Target { - &self.0 + &self.1 } } impl DerefMut for NamespaceRef { fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.0 + &mut self.1 } } impl fmt::Display for NamespaceRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for Ident { name, .. } in self.0.iter() { + for Ident { name, .. } in self.1.iter() { write!(f, "{}{}", name, Token::DoubleColon.syntax())?; } Ok(()) @@ -1961,7 +1961,7 @@ impl fmt::Display for NamespaceRef { impl From> for NamespaceRef { fn from(modules: StaticVec) -> Self { - Self(modules, None) + Self(None, modules) } } @@ -1991,12 +1991,14 @@ impl> AddAssign for Module { } impl NamespaceRef { + /// Get the [`Scope`][crate::Scope] index offset. pub(crate) fn index(&self) -> Option { - self.1 + self.0 } + /// Set the [`Scope`][crate::Scope] index offset. #[cfg(not(feature = "no_module"))] pub(crate) fn set_index(&mut self, index: Option) { - self.1 = index + self.0 = index } } diff --git a/src/optimize.rs b/src/optimize.rs index ce0d26b9..7c032d30 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -717,12 +717,12 @@ fn optimize_expr(expr: &mut Expr, state: &mut State) { Expr::FnCall(x, _) => x.args.iter_mut().for_each(|a| optimize_expr(a, state)), // constant-name - Expr::Variable(x) if x.1.is_none() && state.find_constant(&x.3.name).is_some() => { + Expr::Variable(x) if x.1.is_none() && state.find_constant(&x.2.name).is_some() => { state.set_dirty(); // Replace constant with value - let mut result = state.find_constant(&x.3.name).unwrap().clone(); - result.set_position(x.3.pos); + let mut result = state.find_constant(&x.2.name).unwrap().clone(); + result.set_position(x.2.pos); *expr = result; } diff --git a/src/parser.rs b/src/parser.rs index cf0e9d4f..342fe94c 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -238,7 +238,7 @@ impl Expr { fn into_property(self, state: &mut ParseState) -> Self { match self { Self::Variable(x) if x.1.is_none() => { - let ident = x.3; + let ident = x.2; let getter = state.get_interned_string(crate::engine::make_getter(&ident.name)); let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); Self::Property(Box::new(((getter, setter), ident.into()))) @@ -310,7 +310,7 @@ fn parse_fn_call( lib: &mut FunctionsLib, id: ImmutableString, capture: bool, - mut namespace: Option>, + mut namespace: Option, settings: ParseSettings, ) -> Result { let (token, token_pos) = input.peek().unwrap(); @@ -335,7 +335,7 @@ fn parse_fn_call( Token::RightParen => { eat_token(input, Token::RightParen); - let mut hash_script = if let Some(modules) = namespace.as_mut() { + let mut hash_script = if let Some(ref mut modules) = namespace { #[cfg(not(feature = "no_module"))] modules.set_index(state.find_module(&modules[0].name)); @@ -981,7 +981,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, None, var_name_def))) + Expr::Variable(Box::new((None, None, var_name_def))) } // Namespace qualification #[cfg(not(feature = "no_module"))] @@ -995,7 +995,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, None, var_name_def))) + Expr::Variable(Box::new((None, None, var_name_def))) } // Normal variable access Token::Identifier(s) => { @@ -1004,7 +1004,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((index, None, None, var_name_def))) + Expr::Variable(Box::new((index, None, var_name_def))) } // Function call is allowed to have reserved keyword @@ -1014,7 +1014,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, None, var_name_def))) + Expr::Variable(Box::new((None, None, var_name_def))) } else { return Err(PERR::Reserved(s).into_err(settings.pos)); } @@ -1030,7 +1030,7 @@ fn parse_primary( name: state.get_interned_string(s), pos: settings.pos, }; - Expr::Variable(Box::new((None, None, None, var_name_def))) + Expr::Variable(Box::new((None, None, var_name_def))) } } @@ -1089,35 +1089,38 @@ fn parse_primary( .into_err(pos)); } - let (_, modules, _, Ident { name, pos }) = *x; + let (_, namespace, Ident { name, pos }) = *x; settings.pos = pos; - parse_fn_call(input, state, lib, name, true, modules, settings.level_up())? + let ns = namespace.map(|(_, ns)| ns); + parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? } // Function call (Expr::Variable(x), Token::LeftParen) => { - let (_, modules, _, Ident { name, pos }) = *x; + let (_, namespace, Ident { name, pos }) = *x; settings.pos = pos; - parse_fn_call(input, state, lib, name, false, modules, settings.level_up())? + let ns = namespace.map(|(_, ns)| ns); + parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? } (Expr::Property(_), _) => unreachable!(), // module access (Expr::Variable(x), Token::DoubleColon) => match input.next().unwrap() { (Token::Identifier(id2), pos2) => { - let (index, mut modules, _, var_name_def) = *x; + let (index, mut namespace, var_name_def) = *x; - if let Some(ref mut modules) = modules { - modules.push(var_name_def); + if let Some((_, ref mut namespace)) = namespace { + namespace.push(var_name_def); } else { - let mut m: NamespaceRef = Default::default(); - m.push(var_name_def); - modules = Some(Box::new(m)); + let mut ns: NamespaceRef = Default::default(); + ns.push(var_name_def); + let index = NonZeroU64::new(42).unwrap(); // Dummy + namespace = Some((index, ns)); } let var_name_def = Ident { name: state.get_interned_string(id2), pos: pos2, }; - Expr::Variable(Box::new((index, modules, None, var_name_def))) + Expr::Variable(Box::new((index, namespace, var_name_def))) } (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { return Err(PERR::Reserved(id2).into_err(pos2)); @@ -1154,14 +1157,16 @@ fn parse_primary( _ => None, } .map(|x| { - let (_, modules, hash, Ident { name, .. }) = x.as_mut(); - let namespace = modules.as_mut().unwrap(); + if let (_, Some((ref mut hash, ref mut namespace)), Ident { name, .. }) = x.as_mut() { + // Qualifiers + variable name + *hash = + calc_script_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0).unwrap(); - // Qualifiers + variable name - *hash = calc_script_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0); - - #[cfg(not(feature = "no_module"))] - namespace.set_index(state.find_module(&namespace[0].name)); + #[cfg(not(feature = "no_module"))] + namespace.set_index(state.find_module(&namespace[0].name)); + } else { + unreachable!(); + } }); // Make sure identifiers are valid @@ -1335,7 +1340,7 @@ fn make_assignment_stmt<'a>( )), // var (indexed) = rhs Expr::Variable(x) => { - let (index, _, _, Ident { name, pos }) = x.as_ref(); + let (index, _, Ident { name, pos }) = x.as_ref(); match state.stack[(state.stack.len() - index.unwrap().get())].1 { AccessMode::ReadWrite => Ok(Stmt::Assignment( Box::new((lhs, fn_name.into(), rhs)), @@ -1356,7 +1361,7 @@ fn make_assignment_stmt<'a>( )), // var[???] (indexed) = rhs, var.??? (indexed) = rhs Expr::Variable(x) => { - let (index, _, _, Ident { name, pos }) = x.as_ref(); + let (index, _, Ident { name, pos }) = x.as_ref(); match state.stack[(state.stack.len() - index.unwrap().get())].1 { AccessMode::ReadWrite => Ok(Stmt::Assignment( Box::new((lhs, fn_name.into(), rhs)), @@ -1440,7 +1445,7 @@ fn make_dot_expr( } // lhs.id (lhs, Expr::Variable(x)) if x.1.is_none() => { - let ident = x.3; + let ident = x.2; let getter = state.get_interned_string(crate::engine::make_getter(&ident.name)); let setter = state.get_interned_string(crate::engine::make_setter(&ident.name)); let rhs = Expr::Property(Box::new(((getter, setter), ident))); @@ -1449,7 +1454,7 @@ fn make_dot_expr( } // lhs.module::id - syntax error (_, Expr::Variable(x)) if x.1.is_some() => { - return Err(PERR::PropertyExpected.into_err(x.1.unwrap()[0].pos)); + return Err(PERR::PropertyExpected.into_err(x.1.unwrap().1[0].pos)); } // lhs.prop (lhs, prop @ Expr::Property(_)) => { @@ -1877,7 +1882,7 @@ fn parse_custom_syntax( segments.push(name.clone()); tokens.push(state.get_interned_string(MARKER_IDENT)); let var_name_def = Ident { name, pos }; - keywords.push(Expr::Variable(Box::new((None, None, None, var_name_def)))); + keywords.push(Expr::Variable(Box::new((None, None, var_name_def)))); } (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2797,14 +2802,12 @@ fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Po #[cfg(not(feature = "no_closure"))] externals.iter().for_each(|x| { - let expr = Expr::Variable(Box::new((None, None, None, x.clone().into()))); - args.push(expr); + args.push(Expr::Variable(Box::new((None, None, x.clone().into())))); }); #[cfg(feature = "no_closure")] externals.into_iter().for_each(|x| { - let expr = Expr::Variable(Box::new((None, None, None, x.clone().into()))); - args.push(expr); + args.push(Expr::Variable(Box::new((None, None, x.clone().into())))); }); let curry_func = crate::engine::KEYWORD_FN_PTR_CURRY; diff --git a/src/utils.rs b/src/utils.rs index 17721e47..5a9be55d 100644 --- a/src/utils.rs +++ b/src/utils.rs @@ -22,23 +22,21 @@ use crate::Shared; /// /// Panics when hashing any data type other than a [`NonZeroU64`]. #[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct StraightHasher(u64); +pub struct StraightHasher(NonZeroU64); impl Hasher for StraightHasher { #[inline(always)] fn finish(&self) -> u64 { - self.0 + self.0.get() } #[inline(always)] fn write(&mut self, bytes: &[u8]) { let mut key = [0_u8; 8]; key.copy_from_slice(&bytes[..8]); // Panics if fewer than 8 bytes - self.0 = u64::from_le_bytes(key); // HACK - If it so happens to hash directly to zero (OMG!) then change it to 42... - if self.0 == 0 { - self.0 = 42; - } + self.0 = NonZeroU64::new(u64::from_le_bytes(key)) + .unwrap_or_else(|| NonZeroU64::new(42).unwrap()); } } @@ -51,7 +49,7 @@ impl BuildHasher for StraightHasherBuilder { #[inline(always)] fn build_hasher(&self) -> Self::Hasher { - StraightHasher(1) + StraightHasher(NonZeroU64::new(42).unwrap()) } }