Streamline string handling.

This commit is contained in:
Stephen Chung 2021-03-24 13:17:52 +08:00
parent 3a1e93e324
commit c4fe1782df
12 changed files with 129 additions and 82 deletions

View File

@ -293,7 +293,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
Ok((engine_expr, export_name, rust_mod_path)) => { Ok((engine_expr, export_name, rust_mod_path)) => {
let gen_mod_path = crate::register::generated_module_path(&rust_mod_path); let gen_mod_path = crate::register::generated_module_path(&rust_mod_path);
proc_macro::TokenStream::from(quote! { proc_macro::TokenStream::from(quote! {
#engine_expr.register_result_fn(&(#export_name), #gen_mod_path::dynamic_result_fn); #engine_expr.register_result_fn(#export_name, #gen_mod_path::dynamic_result_fn);
}) })
} }
Err(e) => e.to_compile_error().into(), Err(e) => e.to_compile_error().into(),

View File

@ -21,7 +21,7 @@ fn count_string_bytes(s: &str) -> INT {
/// This version uses `ImmutableString` and `&str`. /// This version uses `ImmutableString` and `&str`.
fn find_substring(s: ImmutableString, sub: &str) -> INT { fn find_substring(s: ImmutableString, sub: &str) -> INT {
s.as_str().find(sub).map(|x| x as INT).unwrap_or(-1) s.find(sub).map(|x| x as INT).unwrap_or(-1)
} }
fn main() -> Result<(), Box<EvalAltResult>> { fn main() -> Result<(), Box<EvalAltResult>> {

View File

@ -6,7 +6,7 @@ use crate::module::NamespaceRef;
use crate::stdlib::{ use crate::stdlib::{
borrow::Cow, borrow::Cow,
boxed::Box, boxed::Box,
collections::BTreeMap, collections::{BTreeMap, BTreeSet},
fmt, fmt,
hash::Hash, hash::Hash,
num::NonZeroUsize, num::NonZeroUsize,
@ -58,7 +58,7 @@ pub struct ScriptFnDef {
pub params: StaticVec<ImmutableString>, pub params: StaticVec<ImmutableString>,
/// Access to external variables. /// Access to external variables.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
pub externals: StaticVec<ImmutableString>, pub externals: BTreeSet<ImmutableString>,
/// Function doc-comments (if any). /// Function doc-comments (if any).
pub comments: StaticVec<String>, pub comments: StaticVec<String>,
} }

View File

@ -1349,7 +1349,7 @@ impl Dynamic {
} }
if TypeId::of::<T>() == TypeId::of::<String>() { if TypeId::of::<T>() == TypeId::of::<String>() {
return match &self.0 { return match &self.0 {
Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref()), Union::Str(value, _) => <dyn Any>::downcast_ref::<T>(value.as_ref() as &String),
_ => None, _ => None,
}; };
} }

View File

@ -75,7 +75,7 @@ impl Imports {
.iter() .iter()
.enumerate() .enumerate()
.rev() .rev()
.find_map(|(i, key)| if key.as_str() == name { Some(i) } else { None }) .find_map(|(i, key)| if *key == name { Some(i) } else { None })
} }
/// Push an imported [modules][Module] onto the stack. /// Push an imported [modules][Module] onto the stack.
#[inline(always)] #[inline(always)]
@ -996,7 +996,7 @@ impl Engine {
}; };
// Check if the variable is `this` // Check if the variable is `this`
if name.as_str() == KEYWORD_THIS { if *name == KEYWORD_THIS {
return if let Some(val) = this_ptr { return if let Some(val) = this_ptr {
Ok(((*val).into(), *pos)) Ok(((*val).into(), *pos))
} else { } else {

View File

@ -13,8 +13,8 @@ use crate::stdlib::{
vec::Vec, vec::Vec,
}; };
use crate::{ use crate::{
scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext, scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, ImmutableString, Module,
ParseError, Position, RhaiResult, Shared, StaticVec, AST, NativeCallContext, ParseError, Position, RhaiResult, Shared, StaticVec, AST,
}; };
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
@ -52,7 +52,11 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline] #[inline]
pub fn register_fn<A, F>(&mut self, name: &str, func: F) -> &mut Self pub fn register_fn<A, F>(
&mut self,
name: impl AsRef<str> + Into<ImmutableString>,
func: F,
) -> &mut Self
where where
F: RegisterNativeFunction<A, ()>, F: RegisterNativeFunction<A, ()>,
{ {
@ -102,7 +106,11 @@ impl Engine {
/// .expect_err("expecting division by zero error!"); /// .expect_err("expecting division by zero error!");
/// ``` /// ```
#[inline] #[inline]
pub fn register_result_fn<A, F, R>(&mut self, name: &str, func: F) -> &mut Self pub fn register_result_fn<A, F, R>(
&mut self,
name: impl AsRef<str> + Into<ImmutableString>,
func: F,
) -> &mut Self
where where
F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>, F: RegisterNativeFunction<A, Result<R, Box<EvalAltResult>>>,
{ {
@ -144,7 +152,7 @@ impl Engine {
#[inline(always)] #[inline(always)]
pub fn register_raw_fn<T: Variant + Clone>( pub fn register_raw_fn<T: Variant + Clone>(
&mut self, &mut self,
name: &str, name: impl AsRef<str> + Into<ImmutableString>,
arg_types: &[TypeId], arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
@ -878,25 +886,29 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
pub fn register_static_module(&mut self, name: &str, module: Shared<Module>) -> &mut Self { pub fn register_static_module(
&mut self,
name: impl AsRef<str> + Into<ImmutableString>,
module: Shared<Module>,
) -> &mut Self {
fn register_static_module_raw( fn register_static_module_raw(
root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>, root: &mut crate::stdlib::collections::BTreeMap<crate::ImmutableString, Shared<Module>>,
name: &str, name: impl AsRef<str> + Into<ImmutableString>,
module: Shared<Module>, module: Shared<Module>,
) { ) {
let separator = crate::token::Token::DoubleColon.syntax(); let separator = crate::token::Token::DoubleColon.syntax();
if !name.contains(separator.as_ref()) { if !name.as_ref().contains(separator.as_ref()) {
if !module.is_indexed() { if !module.is_indexed() {
// Index the module (making a clone copy if necessary) if it is not indexed // Index the module (making a clone copy if necessary) if it is not indexed
let mut module = crate::fn_native::shared_take_or_clone(module); let mut module = crate::fn_native::shared_take_or_clone(module);
module.build_index(); module.build_index();
root.insert(name.trim().into(), module.into()); root.insert(name.into(), module.into());
} else { } else {
root.insert(name.trim().into(), module); root.insert(name.into(), module);
} }
} else { } else {
let mut iter = name.splitn(2, separator.as_ref()); let mut iter = name.as_ref().splitn(2, separator.as_ref());
let sub_module = iter.next().unwrap().trim(); let sub_module = iter.next().unwrap().trim();
let remainder = iter.next().unwrap().trim(); let remainder = iter.next().unwrap().trim();
@ -915,7 +927,7 @@ impl Engine {
} }
} }
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module); register_static_module_raw(&mut self.global_sub_modules, name, module);
self self
} }
@ -927,7 +939,11 @@ impl Engine {
#[cfg(not(feature = "no_module"))] #[cfg(not(feature = "no_module"))]
#[inline(always)] #[inline(always)]
#[deprecated = "use `register_static_module` instead"] #[deprecated = "use `register_static_module` instead"]
pub fn register_module(&mut self, name: &str, module: impl Into<Shared<Module>>) -> &mut Self { pub fn register_module(
&mut self,
name: impl AsRef<str> + Into<ImmutableString>,
module: impl Into<Shared<Module>>,
) -> &mut Self {
self.register_static_module(name, module.into()) self.register_static_module(name, module.into())
} }
/// Compile a string into an [`AST`], which can be used later for evaluation. /// Compile a string into an [`AST`], which can be used later for evaluation.
@ -1013,7 +1029,6 @@ impl Engine {
fn_native::shared_take_or_clone, fn_native::shared_take_or_clone,
module::resolvers::StaticModuleResolver, module::resolvers::StaticModuleResolver,
stdlib::collections::BTreeSet, stdlib::collections::BTreeSet,
ImmutableString,
}; };
fn collect_imports( fn collect_imports(
@ -1271,9 +1286,14 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_object"))]
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> { pub fn parse_json(
&self,
json: impl AsRef<str>,
has_null: bool,
) -> Result<Map, Box<EvalAltResult>> {
use crate::token::Token; use crate::token::Token;
let json = json.as_ref();
let mut scope = Default::default(); let mut scope = Default::default();
// Trims the JSON string and add a '#' in front // Trims the JSON string and add a '#' in front
@ -1765,7 +1785,7 @@ impl Engine {
&self, &self,
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
name: &str, name: impl AsRef<str>,
args: impl crate::fn_args::FuncArgs, args: impl crate::fn_args::FuncArgs,
) -> Result<T, Box<EvalAltResult>> { ) -> Result<T, Box<EvalAltResult>> {
let mut arg_values: crate::StaticVec<_> = Default::default(); let mut arg_values: crate::StaticVec<_> = Default::default();
@ -1844,7 +1864,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
eval_ast: bool, eval_ast: bool,
name: &str, name: impl AsRef<str>,
mut this_ptr: Option<&mut Dynamic>, mut this_ptr: Option<&mut Dynamic>,
mut arg_values: impl AsMut<[Dynamic]>, mut arg_values: impl AsMut<[Dynamic]>,
) -> RhaiResult { ) -> RhaiResult {
@ -1867,7 +1887,7 @@ impl Engine {
scope: &mut Scope, scope: &mut Scope,
ast: &AST, ast: &AST,
eval_ast: bool, eval_ast: bool,
name: &str, name: impl AsRef<str>,
this_ptr: &mut Option<&mut Dynamic>, this_ptr: &mut Option<&mut Dynamic>,
args: &mut FnCallArgs, args: &mut FnCallArgs,
) -> RhaiResult { ) -> RhaiResult {
@ -1881,12 +1901,14 @@ impl Engine {
let fn_def = ast let fn_def = ast
.lib() .lib()
.get_script_fn(name, args.len()) .get_script_fn(name.as_ref(), args.len())
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::NONE))?; .ok_or_else(|| {
EvalAltResult::ErrorFunctionNotFound(name.as_ref().into(), Position::NONE)
})?;
// Check for data race. // Check for data race.
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
crate::fn_call::ensure_no_data_race(name, args, false)?; crate::fn_call::ensure_no_data_race(name.as_ref(), args, false)?;
self.call_script_fn( self.call_script_fn(
scope, scope,

View File

@ -236,7 +236,7 @@ impl Engine {
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn disable_symbol(&mut self, symbol: &str) -> &mut Self { pub fn disable_symbol(&mut self, symbol: impl Into<String>) -> &mut Self {
self.disabled_symbols.insert(symbol.into()); self.disabled_symbols.insert(symbol.into());
self self
} }
@ -270,7 +270,7 @@ impl Engine {
/// ``` /// ```
pub fn register_custom_operator( pub fn register_custom_operator(
&mut self, &mut self,
keyword: &str, keyword: impl AsRef<str> + Into<String>,
precedence: u8, precedence: u8,
) -> Result<&mut Self, String> { ) -> Result<&mut Self, String> {
let precedence = Precedence::new(precedence); let precedence = Precedence::new(precedence);
@ -279,25 +279,25 @@ impl Engine {
return Err("precedence cannot be zero".into()); return Err("precedence cannot be zero".into());
} }
match Token::lookup_from_syntax(keyword) { match Token::lookup_from_syntax(keyword.as_ref()) {
// Standard identifiers, reserved keywords and custom keywords are OK // Standard identifiers, reserved keywords and custom keywords are OK
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (), None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
// Active standard keywords cannot be made custom // Active standard keywords cannot be made custom
// Disabled keywords are OK // Disabled keywords are OK
Some(token) if token.is_keyword() => { Some(token) if token.is_keyword() => {
if !self.disabled_symbols.contains(token.syntax().as_ref()) { if !self.disabled_symbols.contains(token.syntax().as_ref()) {
return Err(format!("'{}' is a reserved keyword", keyword).into()); return Err(format!("'{}' is a reserved keyword", keyword.as_ref()).into());
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if token.is_symbol() => { Some(token) if token.is_symbol() => {
if !self.disabled_symbols.contains(token.syntax().as_ref()) { if !self.disabled_symbols.contains(token.syntax().as_ref()) {
return Err(format!("'{}' is a reserved operator", keyword).into()); return Err(format!("'{}' is a reserved operator", keyword.as_ref()).into());
} }
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => { Some(token) if !self.disabled_symbols.contains(token.syntax().as_ref()) => {
return Err(format!("'{}' is a reserved symbol", keyword).into()) return Err(format!("'{}' is a reserved symbol", keyword.as_ref()).into())
} }
// Disabled symbols are OK // Disabled symbols are OK
Some(_) => (), Some(_) => (),

View File

@ -475,9 +475,9 @@ impl Engine {
fn_def fn_def
.lib .lib
.as_ref() .as_ref()
.and_then(|m| m.id()) .and_then(|m| m.id().map(|id| id.to_string()))
.unwrap_or_else(|| state.source.as_ref().map_or_else(|| "", |s| s.as_str())) .or_else(|| state.source.as_ref().map(|s| s.to_string()))
.to_string(), .unwrap_or_default(),
err, err,
pos, pos,
) )
@ -651,14 +651,14 @@ impl Engine {
crate::engine::KEYWORD_IS_DEF_FN crate::engine::KEYWORD_IS_DEF_FN
if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() => if args.len() == 2 && args[0].is::<FnPtr>() && args[1].is::<crate::INT>() =>
{ {
let fn_name = args[0].read_lock::<ImmutableString>().unwrap(); let fn_name = &*args[0].read_lock::<ImmutableString>().unwrap();
let num_params = args[1].as_int().unwrap(); let num_params = args[1].as_int().unwrap();
return Ok(( return Ok((
if num_params < 0 { if num_params < 0 {
Dynamic::FALSE Dynamic::FALSE
} else { } else {
let hash_script = calc_fn_hash(empty(), &fn_name, num_params as usize); let hash_script = calc_fn_hash(empty(), fn_name, num_params as usize);
self.has_script_fn(Some(mods), state, lib, hash_script) self.has_script_fn(Some(mods), state, lib, hash_script)
.into() .into()
}, },
@ -737,7 +737,7 @@ impl Engine {
if !func.externals.is_empty() { if !func.externals.is_empty() {
captured captured
.into_iter() .into_iter()
.filter(|(name, _, _)| func.externals.iter().any(|ex| ex == name)) .filter(|(name, _, _)| func.externals.contains(name.as_ref()))
.for_each(|(name, value, _)| { .for_each(|(name, value, _)| {
// Consume the scope values. // Consume the scope values.
scope.push_dynamic(name, value); scope.push_dynamic(name, value);
@ -1219,8 +1219,8 @@ impl Engine {
state state
.source .source
.as_ref() .as_ref()
.map_or_else(|| "", |s| s.as_str()) .map(|s| s.to_string())
.to_string(), .unwrap_or_default(),
err, err,
pos, pos,
)) ))

View File

@ -184,10 +184,12 @@ impl<'a> NativeCallContext<'a> {
#[inline(always)] #[inline(always)]
pub fn call_fn_dynamic_raw( pub fn call_fn_dynamic_raw(
&self, &self,
fn_name: &str, fn_name: impl AsRef<str>,
is_method: bool, is_method: bool,
args: &mut [&mut Dynamic], args: &mut [&mut Dynamic],
) -> RhaiResult { ) -> RhaiResult {
let fn_name = fn_name.as_ref();
let hash = if is_method { let hash = if is_method {
FnCallHash::from_script_and_native( FnCallHash::from_script_and_native(
calc_fn_hash(empty(), fn_name, args.len() - 1), calc_fn_hash(empty(), fn_name, args.len() - 1),

View File

@ -7,7 +7,7 @@ use crate::fn_register::RegisterNativeFunction;
use crate::stdlib::{ use crate::stdlib::{
any::TypeId, any::TypeId,
boxed::Box, boxed::Box,
collections::{BTreeMap, BTreeSet}, collections::BTreeMap,
fmt, format, fmt, format,
iter::empty, iter::empty,
num::NonZeroUsize, num::NonZeroUsize,
@ -16,6 +16,7 @@ use crate::stdlib::{
vec::Vec, vec::Vec,
}; };
use crate::token::Token; use crate::token::Token;
use crate::utils::StringInterner;
use crate::{ use crate::{
calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString, calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, EvalAltResult, ImmutableString,
NativeCallContext, Position, Shared, StaticVec, NativeCallContext, Position, Shared, StaticVec,
@ -114,7 +115,7 @@ impl FuncInfo {
#[inline(always)] #[inline(always)]
fn calc_native_fn_hash<'a>( fn calc_native_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>, modules: impl Iterator<Item = &'a str>,
fn_name: &str, fn_name: impl AsRef<str>,
params: &[TypeId], params: &[TypeId],
) -> u64 { ) -> u64 {
let hash_script = calc_fn_hash(modules, fn_name, params.len()); let hash_script = calc_fn_hash(modules, fn_name, params.len());
@ -148,7 +149,7 @@ pub struct Module {
/// Does the [`Module`] contain indexed functions that have been exposed to the global namespace? /// Does the [`Module`] contain indexed functions that have been exposed to the global namespace?
contains_indexed_global_functions: bool, contains_indexed_global_functions: bool,
/// Interned strings /// Interned strings
interned_strings: BTreeSet<ImmutableString>, interned_strings: StringInterner,
} }
impl Default for Module { impl Default for Module {
@ -361,15 +362,6 @@ impl Module {
self.indexed self.indexed
} }
/// Return an interned string.
fn get_interned_string(&mut self, s: &str) -> ImmutableString {
self.interned_strings.get(s).cloned().unwrap_or_else(|| {
let s: ImmutableString = s.into();
self.interned_strings.insert(s.clone());
s
})
}
/// Generate signatures for all the non-private functions in the [`Module`]. /// Generate signatures for all the non-private functions in the [`Module`].
#[inline(always)] #[inline(always)]
pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ { pub fn gen_fn_signatures(&self) -> impl Iterator<Item = String> + '_ {
@ -625,7 +617,7 @@ impl Module {
pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self { pub fn update_fn_metadata(&mut self, hash_fn: u64, arg_names: &[&str]) -> &mut Self {
let param_names = arg_names let param_names = arg_names
.iter() .iter()
.map(|&name| self.get_interned_string(name)) .map(|&name| self.interned_strings.get(name))
.collect(); .collect();
if let Some(f) = self.functions.get_mut(&hash_fn) { if let Some(f) = self.functions.get_mut(&hash_fn) {
@ -657,7 +649,7 @@ impl Module {
#[inline] #[inline]
pub fn set_fn( pub fn set_fn(
&mut self, &mut self,
name: &str, name: impl AsRef<str> + Into<ImmutableString>,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
arg_names: Option<&[&str]>, arg_names: Option<&[&str]>,
@ -690,11 +682,11 @@ impl Module {
let hash_fn = calc_native_fn_hash(empty(), &name, &param_types); let hash_fn = calc_native_fn_hash(empty(), &name, &param_types);
let name = self.get_interned_string(name); let name = self.interned_strings.get(name);
let param_names = arg_names let param_names = arg_names
.iter() .iter()
.flat_map(|p| p.iter()) .flat_map(|p| p.iter())
.map(|&name| self.get_interned_string(name)) .map(|&arg| self.interned_strings.get(arg))
.collect(); .collect();
self.functions.insert( self.functions.insert(
@ -783,16 +775,21 @@ impl Module {
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_raw_fn<T: Variant + Clone>( pub fn set_raw_fn<N, T, F>(
&mut self, &mut self,
name: &str, name: N,
namespace: FnNamespace, namespace: FnNamespace,
access: FnAccess, access: FnAccess,
arg_types: &[TypeId], arg_types: &[TypeId],
func: impl Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>> func: F,
) -> u64
where
N: AsRef<str> + Into<ImmutableString>,
T: Variant + Clone,
F: Fn(NativeCallContext, &mut FnCallArgs) -> Result<T, Box<EvalAltResult>>
+ SendSync + SendSync
+ 'static, + 'static,
) -> u64 { {
let f = let f =
move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from); move |ctx: NativeCallContext, args: &mut FnCallArgs| func(ctx, args).map(Dynamic::from);
@ -830,8 +827,9 @@ impl Module {
/// assert!(module.contains_fn(hash)); /// assert!(module.contains_fn(hash));
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub fn set_native_fn<ARGS, T, F>(&mut self, name: &str, func: F) -> u64 pub fn set_native_fn<ARGS, N, T, F>(&mut self, name: N, func: F) -> u64
where where
N: AsRef<str> + Into<ImmutableString>,
T: Variant + Clone, T: Variant + Clone,
F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>, F: RegisterNativeFunction<ARGS, Result<T, Box<EvalAltResult>>>,
{ {
@ -1079,11 +1077,16 @@ impl Module {
/// ``` /// ```
#[cfg(not(feature = "no_index"))] #[cfg(not(feature = "no_index"))]
#[inline(always)] #[inline(always)]
pub fn set_indexer_get_set_fn<A: Variant + Clone, B: Variant + Clone, T: Variant + Clone>( pub fn set_indexer_get_set_fn<A, B, T>(
&mut self, &mut self,
get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static, get_fn: impl Fn(&mut A, B) -> Result<T, Box<EvalAltResult>> + SendSync + 'static,
set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static, set_fn: impl Fn(&mut A, B, T) -> Result<(), Box<EvalAltResult>> + SendSync + 'static,
) -> (u64, u64) { ) -> (u64, u64)
where
A: Variant + Clone,
B: Variant + Clone,
T: Variant + Clone,
{
( (
self.set_indexer_get_fn(get_fn), self.set_indexer_get_fn(get_fn),
self.set_indexer_set_fn(set_fn), self.set_indexer_set_fn(set_fn),

View File

@ -22,7 +22,7 @@ use crate::stdlib::{
}; };
use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT}; use crate::syntax::{CustomSyntax, MARKER_BLOCK, MARKER_EXPR, MARKER_IDENT};
use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream}; use crate::token::{is_keyword_function, is_valid_identifier, Token, TokenStream};
use crate::utils::get_hasher; use crate::utils::{get_hasher, StringInterner};
use crate::{ use crate::{
calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position, calc_fn_hash, Dynamic, Engine, ImmutableString, LexError, ParseError, ParseErrorType, Position,
Scope, Shared, StaticVec, AST, Scope, Shared, StaticVec, AST,
@ -44,7 +44,7 @@ struct ParseState<'e> {
/// Reference to the scripting [`Engine`]. /// Reference to the scripting [`Engine`].
engine: &'e Engine, engine: &'e Engine,
/// Interned strings. /// Interned strings.
interned_strings: BTreeMap<String, ImmutableString>, interned_strings: StringInterner,
/// Encapsulates a local stack with variable names to simulate an actual runtime scope. /// Encapsulates a local stack with variable names to simulate an actual runtime scope.
stack: Vec<(ImmutableString, AccessMode)>, stack: Vec<(ImmutableString, AccessMode)>,
/// Size of the local variables stack upon entry of the current block scope. /// Size of the local variables stack upon entry of the current block scope.
@ -160,24 +160,17 @@ impl<'e> ParseState<'e> {
.iter() .iter()
.rev() .rev()
.enumerate() .enumerate()
.find(|(_, n)| **n == name) .find(|&(_, n)| *n == name)
.and_then(|(i, _)| NonZeroUsize::new(i + 1)) .and_then(|(i, _)| NonZeroUsize::new(i + 1))
} }
/// Get an interned string, creating one if it is not yet interned. /// Get an interned string, creating one if it is not yet interned.
#[inline(always)]
pub fn get_interned_string( pub fn get_interned_string(
&mut self, &mut self,
text: impl AsRef<str> + Into<ImmutableString>, text: impl AsRef<str> + Into<ImmutableString>,
) -> ImmutableString { ) -> ImmutableString {
#[allow(clippy::map_entry)] self.interned_strings.get(text)
if !self.interned_strings.contains_key(text.as_ref()) {
let value = text.into();
self.interned_strings
.insert(value.clone().into(), value.clone());
value
} else {
self.interned_strings.get(text.as_ref()).unwrap().clone()
}
} }
} }

View File

@ -6,6 +6,7 @@ use crate::stdlib::{
borrow::Borrow, borrow::Borrow,
boxed::Box, boxed::Box,
cmp::Ordering, cmp::Ordering,
collections::BTreeSet,
fmt, fmt,
fmt::{Debug, Display}, fmt::{Debug, Display},
hash::{BuildHasher, Hash, Hasher}, hash::{BuildHasher, Hash, Hasher},
@ -70,7 +71,11 @@ pub fn get_hasher() -> ahash::AHasher {
/// ///
/// The first module name is skipped. Hashing starts from the _second_ module in the chain. /// The first module name is skipped. Hashing starts from the _second_ module in the chain.
#[inline(always)] #[inline(always)]
pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, num: usize) -> u64 { pub fn calc_fn_hash<'a>(
modules: impl Iterator<Item = &'a str>,
fn_name: impl AsRef<str>,
num: usize,
) -> u64 {
let s = &mut get_hasher(); let s = &mut get_hasher();
// We always skip the first module // We always skip the first module
@ -80,7 +85,7 @@ pub fn calc_fn_hash<'a>(modules: impl Iterator<Item = &'a str>, fn_name: &str, n
.skip(1) .skip(1)
.for_each(|m| m.hash(s)); .for_each(|m| m.hash(s));
len.hash(s); len.hash(s);
fn_name.hash(s); fn_name.as_ref().hash(s);
num.hash(s); num.hash(s);
s.finish() s.finish()
} }
@ -137,7 +142,7 @@ pub(crate) fn combine_hashes(a: u64, b: u64) -> u64 {
/// assert_ne!(s2.as_str(), s.as_str()); /// assert_ne!(s2.as_str(), s.as_str());
/// assert_eq!(s, "hello, world!"); /// assert_eq!(s, "hello, world!");
/// ``` /// ```
#[derive(Clone, Eq, PartialEq, Ord, PartialOrd, Hash, Default)] #[derive(Clone, Eq, Ord, Hash, Default)]
pub struct ImmutableString(Shared<String>); pub struct ImmutableString(Shared<String>);
impl Deref for ImmutableString { impl Deref for ImmutableString {
@ -156,6 +161,13 @@ impl AsRef<String> for ImmutableString {
} }
} }
impl AsRef<str> for ImmutableString {
#[inline(always)]
fn as_ref(&self) -> &str {
&self.0
}
}
impl Borrow<String> for ImmutableString { impl Borrow<String> for ImmutableString {
#[inline(always)] #[inline(always)]
fn borrow(&self) -> &String { fn borrow(&self) -> &String {
@ -559,7 +571,6 @@ impl PartialEq<ImmutableString> for String {
} }
impl<S: AsRef<str>> PartialOrd<S> for ImmutableString { impl<S: AsRef<str>> PartialOrd<S> for ImmutableString {
#[inline(always)]
fn partial_cmp(&self, other: &S) -> Option<Ordering> { fn partial_cmp(&self, other: &S) -> Option<Ordering> {
self.as_str().partial_cmp(other.as_ref()) self.as_str().partial_cmp(other.as_ref())
} }
@ -594,3 +605,19 @@ impl ImmutableString {
shared_make_mut(&mut self.0) shared_make_mut(&mut self.0)
} }
} }
/// A collection of interned strings.
#[derive(Debug, Clone, Default, Hash)]
pub struct StringInterner(BTreeSet<ImmutableString>);
impl StringInterner {
/// Get an interned string, creating one if it is not yet interned.
#[inline(always)]
pub fn get(&mut self, text: impl AsRef<str> + Into<ImmutableString>) -> ImmutableString {
self.0.get(text.as_ref()).cloned().unwrap_or_else(|| {
let s = text.into();
self.0.insert(s.clone());
s
})
}
}