diff --git a/src/ast.rs b/src/ast.rs index c6b99b03..4414464e 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -1,10 +1,10 @@ //! Module defining the AST (abstract syntax tree). +use crate::calc_fn_hash; use crate::dynamic::Union; use crate::fn_native::shared_make_mut; use crate::module::NamespaceRef; use crate::token::Token; -use crate::utils::calc_fn_hash; use crate::{ Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT, }; @@ -1399,6 +1399,15 @@ pub struct CustomExpr { pub tokens: StaticVec, } +impl CustomExpr { + /// Convert this into a [`Expr::Custom`]. + #[inline(always)] + #[must_use] + pub fn into_custom_syntax_expr(self, pos: Position) -> Expr { + Expr::Custom(self.into(), pos) + } +} + /// _(INTERNALS)_ A binary expression. /// Exported under the `internals` feature only. /// @@ -1564,6 +1573,12 @@ impl FnCallExpr { pub fn is_qualified(&self) -> bool { self.namespace.is_some() } + /// Convert this into a [`FnCall`][Expr::FnCall]. + #[inline(always)] + #[must_use] + pub fn into_fn_call_expr(self, pos: Position) -> Expr { + Expr::FnCall(self.into(), pos) + } } /// A type that wraps a floating-point number and implements [`Hash`]. @@ -1730,7 +1745,7 @@ pub enum Expr { Position, Box<( Option, - Option<(u64, NamespaceRef)>, + Option<(NamespaceRef, u64)>, Identifier, )>, ), diff --git a/src/syntax.rs b/src/custom_syntax.rs similarity index 100% rename from src/syntax.rs rename to src/custom_syntax.rs diff --git a/src/engine.rs b/src/engine.rs index 85255a05..205a8c37 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,7 +1,9 @@ //! Main module defining the script evaluation [`Engine`]. use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt}; +use crate::custom_syntax::CustomSyntax; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; +use crate::fn_hash::get_hasher; use crate::fn_native::{ CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback, }; @@ -9,9 +11,7 @@ use crate::module::NamespaceRef; use crate::optimize::OptimizationLevel; use crate::packages::{Package, StandardPackage}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; -use crate::syntax::CustomSyntax; use crate::token::Token; -use crate::utils::get_hasher; use crate::{ Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope, Shared, StaticVec, INT, @@ -1102,17 +1102,17 @@ impl Engine { // Normal variable access (_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), // Qualified variable - (_, Some((hash_var, modules)), var_name) => { - let module = self.search_imports(mods, state, modules).ok_or_else(|| { + (_, Some((namespace, hash_var)), var_name) => { + let module = self.search_imports(mods, state, namespace).ok_or_else(|| { EvalAltResult::ErrorModuleNotFound( - modules[0].name.to_string(), - modules[0].pos, + namespace[0].name.to_string(), + namespace[0].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, var_name); + *err_name = format!("{}{}", namespace, var_name); } _ => (), } diff --git a/src/engine_api.rs b/src/engine_api.rs index 85c26e13..9aba5f05 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -5,7 +5,7 @@ use crate::engine::{EvalContext, Imports, State}; use crate::fn_native::{FnCallArgs, SendSync}; use crate::fn_register::RegisterNativeFunction; use crate::optimize::OptimizationLevel; -use crate::parser::ParseState; +use crate::parse::ParseState; use crate::{ scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, NativeCallContext, ParseError, Position, RhaiResult, Shared, AST, diff --git a/src/result.rs b/src/error.rs similarity index 100% rename from src/result.rs rename to src/error.rs diff --git a/src/parse_error.rs b/src/error_parsing.rs similarity index 100% rename from src/parse_error.rs rename to src/error_parsing.rs diff --git a/src/fn_hash.rs b/src/fn_hash.rs new file mode 100644 index 00000000..e74d0a9b --- /dev/null +++ b/src/fn_hash.rs @@ -0,0 +1,113 @@ +//! Module containing utilities to hash functions and function calls. + +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + any::TypeId, + hash::{BuildHasher, Hash, Hasher}, + iter::empty, +}; + +/// A hasher that only takes one single [`u64`] and returns it as a hash key. +/// +/// # Panics +/// +/// Panics when hashing any data type other than a [`u64`]. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub struct StraightHasher(u64); + +impl Hasher for StraightHasher { + #[inline(always)] + fn finish(&self) -> u64 { + self.0 + } + #[inline(always)] + fn write(&mut self, bytes: &[u8]) { + assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); + + let mut key = [0_u8; 8]; + key.copy_from_slice(bytes); + + self.0 = u64::from_ne_bytes(key); + } +} + +/// A hash builder for `StraightHasher`. +#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] +pub struct StraightHasherBuilder; + +impl BuildHasher for StraightHasherBuilder { + type Hasher = StraightHasher; + + #[inline(always)] + fn build_hasher(&self) -> Self::Hasher { + StraightHasher(42) + } +} + +/// Create an instance of the default hasher. +#[inline(always)] +#[must_use] +pub fn get_hasher() -> ahash::AHasher { + Default::default() +} + +/// Calculate a [`u64`] hash key from a namespace-qualified function name +/// and the number of parameters, but no parameter types. +/// +/// Module names are passed in via `&str` references from an iterator. +/// Parameter types are passed in via [`TypeId`] values from an iterator. +/// +/// # Note +/// +/// The first module name is skipped. Hashing starts from the _second_ module in the chain. +#[inline] +#[must_use] +pub fn calc_qualified_fn_hash<'a>( + modules: impl Iterator, + fn_name: impl AsRef, + num: usize, +) -> u64 { + let s = &mut get_hasher(); + + // We always skip the first module + let mut len = 0; + modules + .inspect(|_| len += 1) + .skip(1) + .for_each(|m| m.hash(s)); + len.hash(s); + fn_name.as_ref().hash(s); + num.hash(s); + s.finish() +} + +/// Calculate a [`u64`] hash key from a non-namespace-qualified function name +/// and the number of parameters, but no parameter types. +/// +/// Parameter types are passed in via [`TypeId`] values from an iterator. +#[inline(always)] +#[must_use] +pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { + calc_qualified_fn_hash(empty(), fn_name, num) +} + +/// Calculate a [`u64`] hash key from a list of parameter types. +/// +/// Parameter types are passed in via [`TypeId`] values from an iterator. +#[inline] +#[must_use] +pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { + let s = &mut get_hasher(); + let mut len = 0; + params.inspect(|_| len += 1).for_each(|t| t.hash(s)); + len.hash(s); + s.finish() +} + +/// Combine two [`u64`] hashes by taking the XOR of them. +#[inline(always)] +#[must_use] +pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 { + a ^ b +} diff --git a/src/utils.rs b/src/immutable_string.rs similarity index 75% rename from src/utils.rs rename to src/immutable_string.rs index 524c4dcf..212ecc24 100644 --- a/src/utils.rs +++ b/src/immutable_string.rs @@ -1,124 +1,19 @@ -//! Module containing various utility types and functions. +//! The `ImmutableString` type. use crate::fn_native::{shared_make_mut, shared_take}; -use crate::{Identifier, Shared, SmartString}; +use crate::{Shared, SmartString}; #[cfg(feature = "no_std")] use std::prelude::v1::*; use std::{ - any::TypeId, borrow::Borrow, cmp::Ordering, fmt, - hash::{BuildHasher, Hash, Hasher}, - iter::{empty, FromIterator}, + hash::Hash, + iter::FromIterator, ops::{Add, AddAssign, Deref, Sub, SubAssign}, str::FromStr, }; -/// A hasher that only takes one single [`u64`] and returns it as a hash key. -/// -/// # Panics -/// -/// Panics when hashing any data type other than a [`u64`]. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)] -pub struct StraightHasher(u64); - -impl Hasher for StraightHasher { - #[inline(always)] - fn finish(&self) -> u64 { - self.0 - } - #[inline(always)] - fn write(&mut self, bytes: &[u8]) { - assert_eq!(bytes.len(), 8, "StraightHasher can only hash u64 values"); - - let mut key = [0_u8; 8]; - key.copy_from_slice(bytes); - - self.0 = u64::from_ne_bytes(key); - } -} - -/// A hash builder for `StraightHasher`. -#[derive(Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] -pub struct StraightHasherBuilder; - -impl BuildHasher for StraightHasherBuilder { - type Hasher = StraightHasher; - - #[inline(always)] - fn build_hasher(&self) -> Self::Hasher { - StraightHasher(42) - } -} - -/// Create an instance of the default hasher. -#[inline(always)] -#[must_use] -pub fn get_hasher() -> ahash::AHasher { - Default::default() -} - -/// Calculate a [`u64`] hash key from a namespace-qualified function name -/// and the number of parameters, but no parameter types. -/// -/// Module names are passed in via `&str` references from an iterator. -/// Parameter types are passed in via [`TypeId`] values from an iterator. -/// -/// # Note -/// -/// The first module name is skipped. Hashing starts from the _second_ module in the chain. -#[inline] -#[must_use] -pub fn calc_qualified_fn_hash<'a>( - modules: impl Iterator, - fn_name: impl AsRef, - num: usize, -) -> u64 { - let s = &mut get_hasher(); - - // We always skip the first module - let mut len = 0; - modules - .inspect(|_| len += 1) - .skip(1) - .for_each(|m| m.hash(s)); - len.hash(s); - fn_name.as_ref().hash(s); - num.hash(s); - s.finish() -} - -/// Calculate a [`u64`] hash key from a non-namespace-qualified function name -/// and the number of parameters, but no parameter types. -/// -/// Parameter types are passed in via [`TypeId`] values from an iterator. -#[inline(always)] -#[must_use] -pub fn calc_fn_hash(fn_name: impl AsRef, num: usize) -> u64 { - calc_qualified_fn_hash(empty(), fn_name, num) -} - -/// Calculate a [`u64`] hash key from a list of parameter types. -/// -/// Parameter types are passed in via [`TypeId`] values from an iterator. -#[inline] -#[must_use] -pub fn calc_fn_params_hash(params: impl Iterator) -> u64 { - let s = &mut get_hasher(); - let mut len = 0; - params.inspect(|_| len += 1).for_each(|t| t.hash(s)); - len.hash(s); - s.finish() -} - -/// Combine two [`u64`] hashes by taking the XOR of them. -#[inline(always)] -#[must_use] -pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 { - a ^ b -} - /// The system immutable string type. /// /// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` @@ -628,33 +523,3 @@ impl ImmutableString { shared_make_mut(&mut self.0) } } - -/// A factory of identifiers from text strings. -/// -/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], -/// this just returns one because most identifiers in Rhai are short and ASCII-based. -/// -/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a -/// collection of strings and returns shared instances, only creating a new string when it is not -/// yet interned. -#[derive(Debug, Clone, Default, Hash)] -pub struct IdentifierBuilder( - #[cfg(feature = "no_smartstring")] std::collections::BTreeSet, -); - -impl IdentifierBuilder { - /// Get an identifier from a text string. - #[inline(always)] - #[must_use] - pub fn get(&mut self, text: impl AsRef + Into) -> Identifier { - #[cfg(not(feature = "no_smartstring"))] - return text.as_ref().into(); - - #[cfg(feature = "no_smartstring")] - return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| { - let s: Identifier = text.into(); - self.0.insert(s.clone()); - s - }); - } -} diff --git a/src/lib.rs b/src/lib.rs index 2db92c3e..a193bf30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -70,28 +70,29 @@ use std::prelude::v1::*; // Internal modules mod ast; +mod custom_syntax; mod dynamic; mod engine; mod engine_api; mod engine_settings; +mod error; +mod error_parsing; mod fn_args; mod fn_builtin; mod fn_call; mod fn_func; +mod fn_hash; mod fn_native; mod fn_register; +mod immutable_string; mod module; mod optimize; pub mod packages; -mod parse_error; -mod parser; +mod parse; pub mod plugin; -mod result; mod scope; -mod syntax; mod token; mod r#unsafe; -mod utils; type RhaiResult = Result>; @@ -126,17 +127,17 @@ pub type FLOAT = f64; pub type FLOAT = f32; pub use ast::{FnAccess, AST}; +pub use custom_syntax::Expression; pub use dynamic::Dynamic; pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS}; +pub use error::EvalAltResult; +pub use error_parsing::{LexError, ParseError, ParseErrorType}; pub use fn_native::{FnPtr, NativeCallContext}; pub use fn_register::RegisterNativeFunction; +pub use immutable_string::ImmutableString; pub use module::{FnNamespace, Module}; -pub use parse_error::{LexError, ParseError, ParseErrorType}; -pub use result::EvalAltResult; pub use scope::Scope; -pub use syntax::Expression; pub use token::Position; -pub use utils::ImmutableString; /// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// identifiers are ASCII and short, fewer than 23 characters, so they can be stored inline. @@ -169,7 +170,9 @@ pub use fn_native::Shared; #[cfg(not(feature = "no_closure"))] use fn_native::Locked; -pub(crate) use utils::{calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes}; +pub(crate) use fn_hash::{ + calc_fn_hash, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, +}; pub use rhai_codegen::*; diff --git a/src/module/mod.rs b/src/module/mod.rs index a394dcd5..c29fd06f 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -4,8 +4,8 @@ use crate::ast::{FnAccess, Ident}; use crate::dynamic::Variant; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_register::RegisterNativeFunction; +use crate::parse::IdentifierBuilder; use crate::token::Token; -use crate::utils::IdentifierBuilder; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec, diff --git a/src/optimize.rs b/src/optimize.rs index f56de860..8d3054bf 100644 --- a/src/optimize.rs +++ b/src/optimize.rs @@ -4,8 +4,8 @@ use crate::ast::{Expr, OpAssignment, Stmt}; use crate::dynamic::AccessMode; use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; use crate::fn_builtin::get_builtin_binary_op_fn; +use crate::fn_hash::get_hasher; use crate::token::Token; -use crate::utils::get_hasher; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, Module, Position, Scope, StaticVec, AST, @@ -729,7 +729,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let mut result = a.remove(*i as usize); + let mut result = mem::take(&mut a[*i as usize]); result.set_position(*pos); *expr = result; } @@ -740,7 +740,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { // Array literal where everything is pure - promote the indexed item. // All other items can be thrown away. state.set_dirty(); - let mut result = a.remove(a.len() - i.abs() as usize); + let index = a.len() - i.abs() as usize; + let mut result = mem::take(&mut a[index]); result.set_position(*pos); *expr = result; } @@ -797,9 +798,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) { } // `... ${ ... } ...` Expr::InterpolatedString(x) => { - x.iter_mut().for_each(|expr| optimize_expr(expr, state, false)); - - let mut n= 0; + let mut n = 0; // Merge consecutive strings while n < x.len()-1 { diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index 1e790000..efa02f1e 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -9,7 +9,7 @@ use std::prelude::v1::*; use crate::FLOAT; #[cfg(not(feature = "no_float"))] -use crate::result::EvalAltResult; +use crate::error::EvalAltResult; #[cfg(feature = "no_std")] #[cfg(not(feature = "no_float"))] diff --git a/src/parser.rs b/src/parse.rs similarity index 96% rename from src/parser.rs rename to src/parse.rs index fd2d7255..6b443fa4 100644 --- a/src/parser.rs +++ b/src/parse.rs @@ -4,19 +4,18 @@ use crate::ast::{ BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType, ScriptFnDef, Stmt, StmtBlock, }; +use crate::custom_syntax::{ + CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING, +}; use crate::dynamic::{AccessMode, Union}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::module::NamespaceRef; -use crate::optimize::optimize_into_ast; -use crate::optimize::OptimizationLevel; -use crate::syntax::{ - CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING, -}; +use crate::optimize::{optimize_into_ast, OptimizationLevel}; +use crate::fn_hash::get_hasher; use crate::token::{ is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, }; -use crate::utils::{get_hasher, IdentifierBuilder}; use crate::{ calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, ParseErrorType, Position, Scope, Shared, StaticVec, AST, @@ -30,7 +29,7 @@ use std::{ }; #[cfg(not(feature = "no_float"))] -use crate::{syntax::MARKER_FLOAT, FLOAT}; +use crate::{custom_syntax::MARKER_FLOAT, FLOAT}; #[cfg(not(feature = "no_function"))] use crate::FnAccess; @@ -41,6 +40,36 @@ type FunctionsLib = BTreeMap>; const NEVER_ENDS: &str = "never fails because `TokenStream` never ends"; +/// A factory of identifiers from text strings. +/// +/// When [`SmartString`](https://crates.io/crates/smartstring) is used as [`Identifier`], +/// this just returns a copy because most identifiers in Rhai are short and ASCII-based. +/// +/// When [`ImmutableString`] is used as [`Identifier`], this type acts as an interner which keeps a +/// collection of strings and returns shared instances, only creating a new string when it is not +/// yet interned. +#[derive(Debug, Clone, Default, Hash)] +pub struct IdentifierBuilder( + #[cfg(feature = "no_smartstring")] std::collections::BTreeSet, +); + +impl IdentifierBuilder { + /// Get an identifier from a text string. + #[inline(always)] + #[must_use] + pub fn get(&mut self, text: impl AsRef + Into) -> Identifier { + #[cfg(not(feature = "no_smartstring"))] + return text.into(); + + #[cfg(feature = "no_smartstring")] + return self.0.get(text.as_ref()).cloned().unwrap_or_else(|| { + let s: Identifier = text.into(); + self.0.insert(s.clone()); + s + }); + } +} + /// A type that encapsulates the current state of the parser. #[derive(Debug)] pub struct ParseState<'e> { @@ -372,17 +401,15 @@ fn parse_fn_call( args.shrink_to_fit(); - return Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier(id), - capture, - namespace, - hashes, - args, - ..Default::default() - }), - settings.pos, - )); + return Ok(FnCallExpr { + name: state.get_identifier(id), + capture, + namespace, + hashes, + args, + ..Default::default() + } + .into_fn_call_expr(settings.pos)); } // id... _ => (), @@ -424,17 +451,15 @@ fn parse_fn_call( args.shrink_to_fit(); - return Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier(id), - capture, - namespace, - hashes, - args, - ..Default::default() - }), - settings.pos, - )); + return Ok(FnCallExpr { + name: state.get_identifier(id), + capture, + namespace, + hashes, + args, + ..Default::default() + } + .into_fn_call_expr(settings.pos)); } // id(...args, (Token::Comma, _) => { @@ -692,7 +717,7 @@ fn parse_array_literal( arr.shrink_to_fit(); - Ok(Expr::Array(Box::new(arr), settings.pos)) + Ok(Expr::Array(arr.into(), settings.pos)) } /// Parse a map literal. @@ -1101,7 +1126,7 @@ fn parse_primary( } segments.shrink_to_fit(); - Expr::InterpolatedString(Box::new(segments)) + Expr::InterpolatedString(segments.into()) } // Array literal @@ -1245,14 +1270,14 @@ fn parse_primary( let (_, namespace, name) = *x; settings.pos = var_pos; - let ns = namespace.map(|(_, ns)| ns); + let ns = namespace.map(|(ns, _)| ns); parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? } // Function call (Expr::Variable(_, var_pos, x), Token::LeftParen) => { let (_, namespace, name) = *x; settings.pos = var_pos; - let ns = namespace.map(|(_, ns)| ns); + let ns = namespace.map(|(ns, _)| ns); parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? } // module access @@ -1264,12 +1289,12 @@ fn parse_primary( pos: var_pos, }; - if let Some((_, ref mut namespace)) = namespace { + if let Some((ref mut namespace, _)) = namespace { namespace.push(var_name_def); } else { let mut ns: NamespaceRef = Default::default(); ns.push(var_name_def); - namespace = Some((42, ns)); + namespace = Some((ns, 42)); } Expr::Variable( @@ -1322,7 +1347,7 @@ fn parse_primary( _ => None, } .map(|x| match x { - (_, Some((hash, namespace)), name) => { + (_, Some((namespace, hash)), name) => { *hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0); #[cfg(not(feature = "no_module"))] @@ -1376,15 +1401,13 @@ fn parse_unary( args.push(expr); args.shrink_to_fit(); - Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier("-"), - hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), - args, - ..Default::default() - }), - pos, - )) + Ok(FnCallExpr { + name: state.get_identifier("-"), + hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), + args, + ..Default::default() + } + .into_fn_call_expr(pos)) } } } @@ -1403,15 +1426,13 @@ fn parse_unary( args.push(expr); args.shrink_to_fit(); - Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier("+"), - hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), - args, - ..Default::default() - }), - pos, - )) + Ok(FnCallExpr { + name: state.get_identifier("+"), + hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), + args, + ..Default::default() + } + .into_fn_call_expr(pos)) } } } @@ -1422,15 +1443,13 @@ fn parse_unary( args.push(parse_unary(input, state, lib, settings.level_up())?); args.shrink_to_fit(); - Ok(Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier("!"), - hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), - args, - ..Default::default() - }), - pos, - )) + Ok(FnCallExpr { + name: state.get_identifier("!"), + hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), + args, + ..Default::default() + } + .into_fn_call_expr(pos)) } // Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)), @@ -1587,7 +1606,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.expect("never fails because the namespace is `Some`").1[0].pos)) + .into_err(x.1.expect("never fails because the namespace is `Some`").0[0].pos)) } // lhs.prop (lhs, prop @ Expr::Property(_)) => { @@ -1772,19 +1791,17 @@ fn parse_binary_op( | Token::PowerOf | Token::Ampersand | Token::Pipe - | Token::XOr => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), + | Token::XOr => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), // '!=' defaults to true when passed invalid operands - Token::NotEqualsTo => Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos), + Token::NotEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), // Comparison operators default to false when passed invalid operands Token::EqualsTo | Token::LessThan | Token::LessThanEqualsTo | Token::GreaterThan - | Token::GreaterThanEqualsTo => { - Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos) - } + | Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos), Token::Or => { let rhs = args @@ -1823,16 +1840,13 @@ fn parse_binary_op( args.shrink_to_fit(); // Convert into a call to `contains` - let hash = calc_fn_hash(OP_CONTAINS, 2); - Expr::FnCall( - Box::new(FnCallExpr { - hashes: FnCallHashes::from_script(hash), - args, - name: state.get_identifier(OP_CONTAINS), - ..op_base - }), - pos, - ) + FnCallExpr { + hashes: FnCallHashes::from_script(calc_fn_hash(OP_CONTAINS, 2)), + args, + name: state.get_identifier(OP_CONTAINS), + ..op_base + } + .into_fn_call_expr(pos) } Token::Custom(s) @@ -1844,18 +1858,16 @@ fn parse_binary_op( { let hash = calc_fn_hash(&s, 2); - Expr::FnCall( - Box::new(FnCallExpr { - hashes: if is_valid_identifier(s.chars()) { - FnCallHashes::from_script(hash) - } else { - FnCallHashes::from_native(hash) - }, - args, - ..op_base - }), - pos, - ) + FnCallExpr { + hashes: if is_valid_identifier(s.chars()) { + FnCallHashes::from_script(hash) + } else { + FnCallHashes::from_native(hash) + }, + args, + ..op_base + } + .into_fn_call_expr(pos) } op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(pos)), @@ -1999,14 +2011,12 @@ fn parse_custom_syntax( keywords.shrink_to_fit(); tokens.shrink_to_fit(); - Ok(Expr::Custom( - Box::new(CustomExpr { - keywords, - tokens, - scope_changed: syntax.scope_changed, - }), - pos, - )) + Ok(CustomExpr { + keywords, + tokens, + scope_changed: syntax.scope_changed, + } + .into_custom_syntax_expr(pos)) } /// Parse an expression. @@ -2942,36 +2952,31 @@ fn make_curry_from_externals( } let num_externals = externals.len(); - let mut args: StaticVec<_> = Default::default(); + let mut args = StaticVec::with_capacity(externals.len() + 1); args.push(fn_expr); - externals.iter().for_each(|x| { - args.push(Expr::Variable( - None, - Position::NONE, - Box::new((None, None, x.clone())), - )); - }); - - args.shrink_to_fit(); - - let expr = Expr::FnCall( - Box::new(FnCallExpr { - name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), - hashes: FnCallHashes::from_native(calc_fn_hash( - crate::engine::KEYWORD_FN_PTR_CURRY, - num_externals + 1, - )), - args, - ..Default::default() - }), - pos, + args.extend( + externals + .iter() + .cloned() + .map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))), ); + let expr = FnCallExpr { + name: state.get_identifier(crate::engine::KEYWORD_FN_PTR_CURRY), + hashes: FnCallHashes::from_native(calc_fn_hash( + crate::engine::KEYWORD_FN_PTR_CURRY, + num_externals + 1, + )), + args, + ..Default::default() + } + .into_fn_call_expr(pos); + // Convert the entire expression into a statement block, then insert the relevant // [`Share`][Stmt::Share] statements. - let mut statements: StaticVec<_> = Default::default(); + let mut statements = StaticVec::with_capacity(externals.len() + 1); statements.extend(externals.into_iter().map(Stmt::Share)); statements.push(Stmt::Expr(expr)); Expr::Stmt(Box::new(StmtBlock::new(statements, pos)))