Make file structures consistent.

This commit is contained in:
Stephen Chung 2021-06-16 18:36:33 +08:00
parent aa2e04bd25
commit 42a14ab4cc
13 changed files with 289 additions and 289 deletions

View File

@ -1,10 +1,10 @@
//! Module defining the AST (abstract syntax tree). //! Module defining the AST (abstract syntax tree).
use crate::calc_fn_hash;
use crate::dynamic::Union; use crate::dynamic::Union;
use crate::fn_native::shared_make_mut; use crate::fn_native::shared_make_mut;
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::token::Token; use crate::token::Token;
use crate::utils::calc_fn_hash;
use crate::{ use crate::{
Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT, Dynamic, FnNamespace, Identifier, ImmutableString, Module, Position, Shared, StaticVec, INT,
}; };
@ -1399,6 +1399,15 @@ pub struct CustomExpr {
pub tokens: StaticVec<Identifier>, pub tokens: StaticVec<Identifier>,
} }
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. /// _(INTERNALS)_ A binary expression.
/// Exported under the `internals` feature only. /// Exported under the `internals` feature only.
/// ///
@ -1564,6 +1573,12 @@ impl FnCallExpr {
pub fn is_qualified(&self) -> bool { pub fn is_qualified(&self) -> bool {
self.namespace.is_some() 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`]. /// A type that wraps a floating-point number and implements [`Hash`].
@ -1730,7 +1745,7 @@ pub enum Expr {
Position, Position,
Box<( Box<(
Option<NonZeroUsize>, Option<NonZeroUsize>,
Option<(u64, NamespaceRef)>, Option<(NamespaceRef, u64)>,
Identifier, Identifier,
)>, )>,
), ),

View File

@ -1,7 +1,9 @@
//! Main module defining the script evaluation [`Engine`]. //! Main module defining the script evaluation [`Engine`].
use crate::ast::{Expr, FnCallExpr, Ident, OpAssignment, ReturnType, Stmt}; 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::dynamic::{map_std_type_name, AccessMode, Union, Variant};
use crate::fn_hash::get_hasher;
use crate::fn_native::{ use crate::fn_native::{
CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback, CallableFunction, IteratorFn, OnDebugCallback, OnPrintCallback, OnVarCallback,
}; };
@ -9,9 +11,7 @@ use crate::module::NamespaceRef;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::packages::{Package, StandardPackage}; use crate::packages::{Package, StandardPackage};
use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime;
use crate::syntax::CustomSyntax;
use crate::token::Token; use crate::token::Token;
use crate::utils::get_hasher;
use crate::{ use crate::{
Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope, Dynamic, EvalAltResult, Identifier, ImmutableString, Module, Position, RhaiResult, Scope,
Shared, StaticVec, INT, Shared, StaticVec, INT,
@ -1102,17 +1102,17 @@ impl Engine {
// Normal variable access // Normal variable access
(_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr), (_, None, _) => self.search_scope_only(scope, mods, state, lib, this_ptr, expr),
// Qualified variable // Qualified variable
(_, Some((hash_var, modules)), var_name) => { (_, Some((namespace, hash_var)), var_name) => {
let module = self.search_imports(mods, state, modules).ok_or_else(|| { let module = self.search_imports(mods, state, namespace).ok_or_else(|| {
EvalAltResult::ErrorModuleNotFound( EvalAltResult::ErrorModuleNotFound(
modules[0].name.to_string(), namespace[0].name.to_string(),
modules[0].pos, namespace[0].pos,
) )
})?; })?;
let target = module.get_qualified_var(*hash_var).map_err(|mut err| { let target = module.get_qualified_var(*hash_var).map_err(|mut err| {
match *err { match *err {
EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => { EvalAltResult::ErrorVariableNotFound(ref mut err_name, _) => {
*err_name = format!("{}{}", modules, var_name); *err_name = format!("{}{}", namespace, var_name);
} }
_ => (), _ => (),
} }

View File

@ -5,7 +5,7 @@ use crate::engine::{EvalContext, Imports, State};
use crate::fn_native::{FnCallArgs, SendSync}; use crate::fn_native::{FnCallArgs, SendSync};
use crate::fn_register::RegisterNativeFunction; use crate::fn_register::RegisterNativeFunction;
use crate::optimize::OptimizationLevel; use crate::optimize::OptimizationLevel;
use crate::parser::ParseState; use crate::parse::ParseState;
use crate::{ use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module, scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Identifier, Module,
NativeCallContext, ParseError, Position, RhaiResult, Shared, AST, NativeCallContext, ParseError, Position, RhaiResult, Shared, AST,

113
src/fn_hash.rs Normal file
View File

@ -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<Item = &'a str>,
fn_name: impl AsRef<str>,
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<str>, 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<Item = TypeId>) -> 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
}

View File

@ -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::fn_native::{shared_make_mut, shared_take};
use crate::{Identifier, Shared, SmartString}; use crate::{Shared, SmartString};
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
use std::prelude::v1::*; use std::prelude::v1::*;
use std::{ use std::{
any::TypeId,
borrow::Borrow, borrow::Borrow,
cmp::Ordering, cmp::Ordering,
fmt, fmt,
hash::{BuildHasher, Hash, Hasher}, hash::Hash,
iter::{empty, FromIterator}, iter::FromIterator,
ops::{Add, AddAssign, Deref, Sub, SubAssign}, ops::{Add, AddAssign, Deref, Sub, SubAssign},
str::FromStr, 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<Item = &'a str>,
fn_name: impl AsRef<str>,
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<str>, 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<Item = TypeId>) -> 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. /// The system immutable string type.
/// ///
/// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>` /// An [`ImmutableString`] wraps an [`Rc`][std::rc::Rc]`<`[`String`]`>`
@ -628,33 +523,3 @@ impl ImmutableString {
shared_make_mut(&mut self.0) 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<Identifier>,
);
impl IdentifierBuilder {
/// Get an identifier from a text string.
#[inline(always)]
#[must_use]
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> 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
});
}
}

View File

@ -70,28 +70,29 @@ use std::prelude::v1::*;
// Internal modules // Internal modules
mod ast; mod ast;
mod custom_syntax;
mod dynamic; mod dynamic;
mod engine; mod engine;
mod engine_api; mod engine_api;
mod engine_settings; mod engine_settings;
mod error;
mod error_parsing;
mod fn_args; mod fn_args;
mod fn_builtin; mod fn_builtin;
mod fn_call; mod fn_call;
mod fn_func; mod fn_func;
mod fn_hash;
mod fn_native; mod fn_native;
mod fn_register; mod fn_register;
mod immutable_string;
mod module; mod module;
mod optimize; mod optimize;
pub mod packages; pub mod packages;
mod parse_error; mod parse;
mod parser;
pub mod plugin; pub mod plugin;
mod result;
mod scope; mod scope;
mod syntax;
mod token; mod token;
mod r#unsafe; mod r#unsafe;
mod utils;
type RhaiResult = Result<Dynamic, Box<EvalAltResult>>; type RhaiResult = Result<Dynamic, Box<EvalAltResult>>;
@ -126,17 +127,17 @@ pub type FLOAT = f64;
pub type FLOAT = f32; pub type FLOAT = f32;
pub use ast::{FnAccess, AST}; pub use ast::{FnAccess, AST};
pub use custom_syntax::Expression;
pub use dynamic::Dynamic; pub use dynamic::Dynamic;
pub use engine::{Engine, EvalContext, OP_CONTAINS, OP_EQUALS}; 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_native::{FnPtr, NativeCallContext};
pub use fn_register::RegisterNativeFunction; pub use fn_register::RegisterNativeFunction;
pub use immutable_string::ImmutableString;
pub use module::{FnNamespace, Module}; pub use module::{FnNamespace, Module};
pub use parse_error::{LexError, ParseError, ParseErrorType};
pub use result::EvalAltResult;
pub use scope::Scope; pub use scope::Scope;
pub use syntax::Expression;
pub use token::Position; pub use token::Position;
pub use utils::ImmutableString;
/// An identifier in Rhai. [`SmartString`](https://crates.io/crates/smartstring) is used because most /// 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. /// 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"))] #[cfg(not(feature = "no_closure"))]
use fn_native::Locked; 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::*; pub use rhai_codegen::*;

View File

@ -4,8 +4,8 @@ use crate::ast::{FnAccess, Ident};
use crate::dynamic::Variant; use crate::dynamic::Variant;
use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync};
use crate::fn_register::RegisterNativeFunction; use crate::fn_register::RegisterNativeFunction;
use crate::parse::IdentifierBuilder;
use crate::token::Token; use crate::token::Token;
use crate::utils::IdentifierBuilder;
use crate::{ use crate::{
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult, calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, EvalAltResult,
Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec, Identifier, ImmutableString, NativeCallContext, Position, Shared, StaticVec,

View File

@ -4,8 +4,8 @@ use crate::ast::{Expr, OpAssignment, Stmt};
use crate::dynamic::AccessMode; use crate::dynamic::AccessMode;
use crate::engine::{KEYWORD_DEBUG, KEYWORD_EVAL, KEYWORD_FN_PTR, KEYWORD_PRINT, KEYWORD_TYPE_OF}; 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_builtin::get_builtin_binary_op_fn;
use crate::fn_hash::get_hasher;
use crate::token::Token; use crate::token::Token;
use crate::utils::get_hasher;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnPtr, ImmutableString,
Module, Position, Scope, StaticVec, AST, 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. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); 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); result.set_position(*pos);
*expr = result; *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. // Array literal where everything is pure - promote the indexed item.
// All other items can be thrown away. // All other items can be thrown away.
state.set_dirty(); 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); result.set_position(*pos);
*expr = result; *expr = result;
} }
@ -797,9 +798,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut State, _chaining: bool) {
} }
// `... ${ ... } ...` // `... ${ ... } ...`
Expr::InterpolatedString(x) => { 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 // Merge consecutive strings
while n < x.len()-1 { while n < x.len()-1 {

View File

@ -9,7 +9,7 @@ use std::prelude::v1::*;
use crate::FLOAT; use crate::FLOAT;
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::result::EvalAltResult; use crate::error::EvalAltResult;
#[cfg(feature = "no_std")] #[cfg(feature = "no_std")]
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]

View File

@ -4,19 +4,18 @@ use crate::ast::{
BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType, BinaryExpr, CustomExpr, Expr, FnCallExpr, FnCallHashes, Ident, OpAssignment, ReturnType,
ScriptFnDef, Stmt, StmtBlock, 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::dynamic::{AccessMode, Union};
use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS};
use crate::module::NamespaceRef; use crate::module::NamespaceRef;
use crate::optimize::optimize_into_ast; use crate::optimize::{optimize_into_ast, OptimizationLevel};
use crate::optimize::OptimizationLevel;
use crate::syntax::{
CustomSyntax, MARKER_BLOCK, MARKER_BOOL, MARKER_EXPR, MARKER_IDENT, MARKER_INT, MARKER_STRING,
};
use crate::fn_hash::get_hasher;
use crate::token::{ use crate::token::{
is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl, is_keyword_function, is_valid_identifier, Token, TokenStream, TokenizerControl,
}; };
use crate::utils::{get_hasher, IdentifierBuilder};
use crate::{ use crate::{
calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError, calc_fn_hash, calc_qualified_fn_hash, Dynamic, Engine, Identifier, LexError, ParseError,
ParseErrorType, Position, Scope, Shared, StaticVec, AST, ParseErrorType, Position, Scope, Shared, StaticVec, AST,
@ -30,7 +29,7 @@ use std::{
}; };
#[cfg(not(feature = "no_float"))] #[cfg(not(feature = "no_float"))]
use crate::{syntax::MARKER_FLOAT, FLOAT}; use crate::{custom_syntax::MARKER_FLOAT, FLOAT};
#[cfg(not(feature = "no_function"))] #[cfg(not(feature = "no_function"))]
use crate::FnAccess; use crate::FnAccess;
@ -41,6 +40,36 @@ type FunctionsLib = BTreeMap<u64, Shared<ScriptFnDef>>;
const NEVER_ENDS: &str = "never fails because `TokenStream` never ends"; 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<Identifier>,
);
impl IdentifierBuilder {
/// Get an identifier from a text string.
#[inline(always)]
#[must_use]
pub fn get(&mut self, text: impl AsRef<str> + Into<Identifier>) -> 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. /// A type that encapsulates the current state of the parser.
#[derive(Debug)] #[derive(Debug)]
pub struct ParseState<'e> { pub struct ParseState<'e> {
@ -372,17 +401,15 @@ fn parse_fn_call(
args.shrink_to_fit(); args.shrink_to_fit();
return Ok(Expr::FnCall( return Ok(FnCallExpr {
Box::new(FnCallExpr { name: state.get_identifier(id),
name: state.get_identifier(id), capture,
capture, namespace,
namespace, hashes,
hashes, args,
args, ..Default::default()
..Default::default() }
}), .into_fn_call_expr(settings.pos));
settings.pos,
));
} }
// id... // id...
_ => (), _ => (),
@ -424,17 +451,15 @@ fn parse_fn_call(
args.shrink_to_fit(); args.shrink_to_fit();
return Ok(Expr::FnCall( return Ok(FnCallExpr {
Box::new(FnCallExpr { name: state.get_identifier(id),
name: state.get_identifier(id), capture,
capture, namespace,
namespace, hashes,
hashes, args,
args, ..Default::default()
..Default::default() }
}), .into_fn_call_expr(settings.pos));
settings.pos,
));
} }
// id(...args, // id(...args,
(Token::Comma, _) => { (Token::Comma, _) => {
@ -692,7 +717,7 @@ fn parse_array_literal(
arr.shrink_to_fit(); arr.shrink_to_fit();
Ok(Expr::Array(Box::new(arr), settings.pos)) Ok(Expr::Array(arr.into(), settings.pos))
} }
/// Parse a map literal. /// Parse a map literal.
@ -1101,7 +1126,7 @@ fn parse_primary(
} }
segments.shrink_to_fit(); segments.shrink_to_fit();
Expr::InterpolatedString(Box::new(segments)) Expr::InterpolatedString(segments.into())
} }
// Array literal // Array literal
@ -1245,14 +1270,14 @@ fn parse_primary(
let (_, namespace, name) = *x; let (_, namespace, name) = *x;
settings.pos = var_pos; 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())? parse_fn_call(input, state, lib, name, true, ns, settings.level_up())?
} }
// Function call // Function call
(Expr::Variable(_, var_pos, x), Token::LeftParen) => { (Expr::Variable(_, var_pos, x), Token::LeftParen) => {
let (_, namespace, name) = *x; let (_, namespace, name) = *x;
settings.pos = var_pos; 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())? parse_fn_call(input, state, lib, name, false, ns, settings.level_up())?
} }
// module access // module access
@ -1264,12 +1289,12 @@ fn parse_primary(
pos: var_pos, pos: var_pos,
}; };
if let Some((_, ref mut namespace)) = namespace { if let Some((ref mut namespace, _)) = namespace {
namespace.push(var_name_def); namespace.push(var_name_def);
} else { } else {
let mut ns: NamespaceRef = Default::default(); let mut ns: NamespaceRef = Default::default();
ns.push(var_name_def); ns.push(var_name_def);
namespace = Some((42, ns)); namespace = Some((ns, 42));
} }
Expr::Variable( Expr::Variable(
@ -1322,7 +1347,7 @@ fn parse_primary(
_ => None, _ => None,
} }
.map(|x| match x { .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); *hash = calc_qualified_fn_hash(namespace.iter().map(|v| v.name.as_str()), name, 0);
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
@ -1376,15 +1401,13 @@ fn parse_unary(
args.push(expr); args.push(expr);
args.shrink_to_fit(); args.shrink_to_fit();
Ok(Expr::FnCall( Ok(FnCallExpr {
Box::new(FnCallExpr { name: state.get_identifier("-"),
name: state.get_identifier("-"), hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)),
hashes: FnCallHashes::from_native(calc_fn_hash("-", 1)), args,
args, ..Default::default()
..Default::default() }
}), .into_fn_call_expr(pos))
pos,
))
} }
} }
} }
@ -1403,15 +1426,13 @@ fn parse_unary(
args.push(expr); args.push(expr);
args.shrink_to_fit(); args.shrink_to_fit();
Ok(Expr::FnCall( Ok(FnCallExpr {
Box::new(FnCallExpr { name: state.get_identifier("+"),
name: state.get_identifier("+"), hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)),
hashes: FnCallHashes::from_native(calc_fn_hash("+", 1)), args,
args, ..Default::default()
..Default::default() }
}), .into_fn_call_expr(pos))
pos,
))
} }
} }
} }
@ -1422,15 +1443,13 @@ fn parse_unary(
args.push(parse_unary(input, state, lib, settings.level_up())?); args.push(parse_unary(input, state, lib, settings.level_up())?);
args.shrink_to_fit(); args.shrink_to_fit();
Ok(Expr::FnCall( Ok(FnCallExpr {
Box::new(FnCallExpr { name: state.get_identifier("!"),
name: state.get_identifier("!"), hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)),
hashes: FnCallHashes::from_native(calc_fn_hash("!", 1)), args,
args, ..Default::default()
..Default::default() }
}), .into_fn_call_expr(pos))
pos,
))
} }
// <EOF> // <EOF>
Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)), Token::EOF => Err(PERR::UnexpectedEOF.into_err(settings.pos)),
@ -1587,7 +1606,7 @@ fn make_dot_expr(
// lhs.module::id - syntax error // lhs.module::id - syntax error
(_, Expr::Variable(_, _, x)) if x.1.is_some() => { (_, Expr::Variable(_, _, x)) if x.1.is_some() => {
return Err(PERR::PropertyExpected 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
(lhs, prop @ Expr::Property(_)) => { (lhs, prop @ Expr::Property(_)) => {
@ -1772,19 +1791,17 @@ fn parse_binary_op(
| Token::PowerOf | Token::PowerOf
| Token::Ampersand | Token::Ampersand
| Token::Pipe | 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 // '!=' 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 // Comparison operators default to false when passed invalid operands
Token::EqualsTo Token::EqualsTo
| Token::LessThan | Token::LessThan
| Token::LessThanEqualsTo | Token::LessThanEqualsTo
| Token::GreaterThan | Token::GreaterThan
| Token::GreaterThanEqualsTo => { | Token::GreaterThanEqualsTo => FnCallExpr { args, ..op_base }.into_fn_call_expr(pos),
Expr::FnCall(Box::new(FnCallExpr { args, ..op_base }), pos)
}
Token::Or => { Token::Or => {
let rhs = args let rhs = args
@ -1823,16 +1840,13 @@ fn parse_binary_op(
args.shrink_to_fit(); args.shrink_to_fit();
// Convert into a call to `contains` // Convert into a call to `contains`
let hash = calc_fn_hash(OP_CONTAINS, 2); FnCallExpr {
Expr::FnCall( hashes: FnCallHashes::from_script(calc_fn_hash(OP_CONTAINS, 2)),
Box::new(FnCallExpr { args,
hashes: FnCallHashes::from_script(hash), name: state.get_identifier(OP_CONTAINS),
args, ..op_base
name: state.get_identifier(OP_CONTAINS), }
..op_base .into_fn_call_expr(pos)
}),
pos,
)
} }
Token::Custom(s) Token::Custom(s)
@ -1844,18 +1858,16 @@ fn parse_binary_op(
{ {
let hash = calc_fn_hash(&s, 2); let hash = calc_fn_hash(&s, 2);
Expr::FnCall( FnCallExpr {
Box::new(FnCallExpr { hashes: if is_valid_identifier(s.chars()) {
hashes: if is_valid_identifier(s.chars()) { FnCallHashes::from_script(hash)
FnCallHashes::from_script(hash) } else {
} else { FnCallHashes::from_native(hash)
FnCallHashes::from_native(hash) },
}, args,
args, ..op_base
..op_base }
}), .into_fn_call_expr(pos)
pos,
)
} }
op_token => return Err(PERR::UnknownOperator(op_token.into()).into_err(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(); keywords.shrink_to_fit();
tokens.shrink_to_fit(); tokens.shrink_to_fit();
Ok(Expr::Custom( Ok(CustomExpr {
Box::new(CustomExpr { keywords,
keywords, tokens,
tokens, scope_changed: syntax.scope_changed,
scope_changed: syntax.scope_changed, }
}), .into_custom_syntax_expr(pos))
pos,
))
} }
/// Parse an expression. /// Parse an expression.
@ -2942,36 +2952,31 @@ fn make_curry_from_externals(
} }
let num_externals = externals.len(); let num_externals = externals.len();
let mut args: StaticVec<_> = Default::default(); let mut args = StaticVec::with_capacity(externals.len() + 1);
args.push(fn_expr); args.push(fn_expr);
externals.iter().for_each(|x| { args.extend(
args.push(Expr::Variable( externals
None, .iter()
Position::NONE, .cloned()
Box::new((None, None, x.clone())), .map(|x| Expr::Variable(None, Position::NONE, Box::new((None, None, x)))),
));
});
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,
); );
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 // Convert the entire expression into a statement block, then insert the relevant
// [`Share`][Stmt::Share] statements. // [`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.extend(externals.into_iter().map(Stmt::Share));
statements.push(Stmt::Expr(expr)); statements.push(Stmt::Expr(expr));
Expr::Stmt(Box::new(StmtBlock::new(statements, pos))) Expr::Stmt(Box::new(StmtBlock::new(statements, pos)))