Reduce size of Engine.

This commit is contained in:
Stephen Chung 2022-11-24 22:58:42 +08:00
parent cefe3f1715
commit 2bf8e610a3
18 changed files with 168 additions and 78 deletions

View File

@ -12,7 +12,7 @@ use crate::{
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{borrow::Borrow, ops::Deref};
use std::{borrow::Borrow, collections::BTreeMap, ops::Deref};
/// Collection of special markers for custom syntax definition.
pub mod markers {
@ -257,19 +257,29 @@ impl Engine {
// Standard or reserved keyword/symbol not in first position
_ if !segments.is_empty() && token.is_some() => {
// Make it a custom keyword/symbol if it is disabled or reserved
if ((!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s))
if (self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s))
|| token.map_or(false, |v| v.is_reserved()))
&& (self.custom_keywords.is_empty()
|| !self.custom_keywords.contains_key(s))
&& !self
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(s))
{
self.custom_keywords.insert(s.into(), None);
self.custom_keywords
.get_or_insert_with(|| BTreeMap::new().into())
.insert(s.into(), None);
}
s.into()
}
// Standard keyword in first position but not disabled
_ if segments.is_empty()
&& token.as_ref().map_or(false, Token::is_standard_keyword)
&& (self.disabled_symbols.is_empty() || !self.disabled_symbols.contains(s)) =>
&& !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s)) =>
{
return Err(LexError::ImproperSymbol(
s.to_string(),
@ -283,12 +293,19 @@ impl Engine {
// Identifier in first position
_ if segments.is_empty() && is_valid_identifier(s) => {
// Make it a custom keyword/symbol if it is disabled or reserved
if (!self.disabled_symbols.is_empty() && self.disabled_symbols.contains(s))
|| token.map_or(false, |v| v.is_reserved())
&& self.custom_keywords.is_empty()
|| !self.custom_keywords.contains_key(s)
if self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s))
|| (token.map_or(false, |v| v.is_reserved())
&& !self
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(s)))
{
self.custom_keywords.insert(s.into(), None);
self.custom_keywords
.get_or_insert_with(|| BTreeMap::new().into())
.insert(s.into(), None);
}
s.into()
}
@ -373,14 +390,16 @@ impl Engine {
scope_may_be_changed: bool,
func: impl Fn(&mut EvalContext, &[Expression], &Dynamic) -> RhaiResult + SendSync + 'static,
) -> &mut Self {
self.custom_syntax.insert(
key.into(),
CustomSyntax {
parse: Box::new(parse),
func: Box::new(func),
scope_may_be_changed,
},
);
self.custom_syntax
.get_or_insert_with(|| BTreeMap::new().into())
.insert(
key.into(),
CustomSyntax {
parse: Box::new(parse),
func: Box::new(func),
scope_may_be_changed,
},
);
self
}
}

View File

@ -369,6 +369,7 @@ impl Definitions<'_> {
.engine
.global_sub_modules
.iter()
.flat_map(|m| m.iter())
.map(move |(name, module)| {
(
name.to_string(),
@ -445,13 +446,16 @@ impl Module {
first = false;
if f.access != FnAccess::Private {
#[cfg(not(feature = "no_custom_syntax"))]
let operator = def.engine.custom_keywords.contains_key(f.name.as_str())
|| (!f.name.contains('$') && !is_valid_function_name(f.name.as_str()));
#[cfg(feature = "no_custom_syntax")]
let operator = !f.name.contains('$') && !is_valid_function_name(&f.name);
#[cfg(not(feature = "no_custom_syntax"))]
let operator = operator
|| def
.engine
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(f.name.as_str()));
f.write_definition(writer, def, operator)?;
}
}

View File

@ -360,7 +360,7 @@ impl Engine {
+ SendSync
+ 'static,
) -> &mut Self {
self.debugger = Some((Box::new(init), Box::new(callback)));
self.debugger = Some(Box::new((Box::new(init), Box::new(callback))));
self
}
}

View File

@ -35,6 +35,7 @@ pub mod definitions;
use crate::{Dynamic, Engine, Identifier};
use std::collections::BTreeSet;
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
@ -107,7 +108,9 @@ impl Engine {
/// ```
#[inline(always)]
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
self.disabled_symbols.insert(symbol.into());
self.disabled_symbols
.get_or_insert_with(|| BTreeSet::new().into())
.insert(symbol.into());
self
}
@ -163,18 +166,31 @@ impl Engine {
// Active standard keywords cannot be made custom
// Disabled keywords are OK
Some(token) if token.is_standard_keyword() => {
if !self.disabled_symbols.contains(token.literal_syntax()) {
if !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{
return Err(format!("'{keyword}' is a reserved keyword"));
}
}
// Active standard symbols cannot be made custom
Some(token) if token.is_standard_symbol() => {
if !self.disabled_symbols.contains(token.literal_syntax()) {
if !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{
return Err(format!("'{keyword}' is a reserved operator"));
}
}
// Active standard symbols cannot be made custom
Some(token) if !self.disabled_symbols.contains(token.literal_syntax()) => {
Some(token)
if !self
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax())) =>
{
return Err(format!("'{keyword}' is a reserved symbol"))
}
// Disabled symbols are OK
@ -183,6 +199,7 @@ impl Engine {
// Add to custom keywords
self.custom_keywords
.get_or_insert_with(|| std::collections::BTreeMap::new().into())
.insert(keyword.into(), Some(precedence));
Ok(self)

View File

@ -683,8 +683,10 @@ impl Engine {
name: impl AsRef<str>,
module: SharedModule,
) -> &mut Self {
use std::collections::BTreeMap;
fn register_static_module_raw(
root: &mut std::collections::BTreeMap<Identifier, SharedModule>,
root: &mut BTreeMap<Identifier, SharedModule>,
name: &str,
module: SharedModule,
) {
@ -717,7 +719,12 @@ impl Engine {
}
}
register_static_module_raw(&mut self.global_sub_modules, name.as_ref(), module);
register_static_module_raw(
self.global_sub_modules
.get_or_insert_with(|| BTreeMap::new().into()),
name.as_ref(),
module,
);
self
}
/// _(metadata)_ Generate a list of all registered functions.
@ -737,7 +744,7 @@ impl Engine {
signatures.extend(self.global_namespace().gen_fn_signatures());
#[cfg(not(feature = "no_module"))]
for (name, m) in &self.global_sub_modules {
for (name, m) in self.global_sub_modules.iter().flat_map(|m| m.iter()) {
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
}

View File

@ -205,6 +205,7 @@ impl Engine {
return self
.global_sub_modules
.iter()
.flat_map(|m| m.iter())
.find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")]
return None;
@ -238,6 +239,7 @@ impl Engine {
return self
.global_sub_modules
.iter()
.flat_map(|m| m.iter())
.find_map(|(_, m)| m.get_custom_type(name));
#[cfg(feature = "no_module")]
return None;

View File

@ -96,7 +96,8 @@ pub struct Engine {
pub(crate) global_modules: StaticVec<SharedModule>,
/// A collection of all sub-modules directly loaded into the Engine.
#[cfg(not(feature = "no_module"))]
pub(crate) global_sub_modules: std::collections::BTreeMap<Identifier, SharedModule>,
pub(crate) global_sub_modules:
Option<Box<std::collections::BTreeMap<Identifier, SharedModule>>>,
/// A module resolution service.
#[cfg(not(feature = "no_module"))]
@ -106,14 +107,16 @@ pub struct Engine {
pub(crate) interned_strings: Locked<StringsInterner>,
/// A set of symbols to disable.
pub(crate) disabled_symbols: BTreeSet<Identifier>,
pub(crate) disabled_symbols: Option<Box<BTreeSet<Identifier>>>,
/// A map containing custom keywords and precedence to recognize.
#[cfg(not(feature = "no_custom_syntax"))]
pub(crate) custom_keywords: std::collections::BTreeMap<Identifier, Option<Precedence>>,
pub(crate) custom_keywords:
Option<Box<std::collections::BTreeMap<Identifier, Option<Precedence>>>>,
/// Custom syntax.
#[cfg(not(feature = "no_custom_syntax"))]
pub(crate) custom_syntax:
std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>,
pub(crate) custom_syntax: Option<
Box<std::collections::BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>>,
>,
/// Callback closure for filtering variable definition.
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
/// Callback closure for resolving variable access.
@ -144,10 +147,12 @@ pub struct Engine {
/// Callback closure for debugging.
#[cfg(feature = "debugging")]
pub(crate) debugger: Option<(
Box<crate::eval::OnDebuggingInit>,
Box<crate::eval::OnDebuggerCallback>,
)>,
pub(crate) debugger: Option<
Box<(
Box<crate::eval::OnDebuggingInit>,
Box<crate::eval::OnDebuggerCallback>,
)>,
>,
}
impl fmt::Debug for Engine {
@ -168,7 +173,8 @@ impl fmt::Debug for Engine {
"custom_syntax",
&self
.custom_syntax
.keys()
.iter()
.flat_map(|m| m.keys())
.map(crate::SmartString::as_str)
.collect::<String>(),
);
@ -264,17 +270,17 @@ impl Engine {
global_modules: StaticVec::new_const(),
#[cfg(not(feature = "no_module"))]
global_sub_modules: std::collections::BTreeMap::new(),
global_sub_modules: None,
#[cfg(not(feature = "no_module"))]
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
interned_strings: StringsInterner::new().into(),
disabled_symbols: BTreeSet::new(),
disabled_symbols: None,
#[cfg(not(feature = "no_custom_syntax"))]
custom_keywords: std::collections::BTreeMap::new(),
custom_keywords: None,
#[cfg(not(feature = "no_custom_syntax"))]
custom_syntax: std::collections::BTreeMap::new(),
custom_syntax: None,
def_var_filter: None,
resolve_var: None,

View File

@ -512,7 +512,9 @@ impl Engine {
let src = src.as_ref().map(|s| s.as_str());
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
if let Some((.., ref on_debugger)) = self.debugger {
if let Some(ref x) = self.debugger {
let (.., ref on_debugger) = **x;
let command = on_debugger(context, event, node, src, node.position())?;
match command {

View File

@ -36,7 +36,12 @@ impl Engine {
// Do a text-match search if the index doesn't work
global.find_import(root).map_or_else(
|| self.global_sub_modules.get(root).cloned(),
|| {
self.global_sub_modules
.as_ref()
.and_then(|m| m.get(root))
.cloned()
},
|offset| global.get_shared_import(offset),
)
}
@ -401,13 +406,17 @@ impl Engine {
// The first token acts as the custom syntax's key
let key_token = custom.tokens.first().unwrap();
// The key should exist, unless the AST is compiled in a different Engine
let custom_def = self.custom_syntax.get(key_token.as_str()).ok_or_else(|| {
Box::new(ERR::ErrorCustomSyntax(
format!("Invalid custom syntax prefix: {key_token}"),
custom.tokens.iter().map(<_>::to_string).collect(),
*pos,
))
})?;
let custom_def = self
.custom_syntax
.as_ref()
.and_then(|m| m.get(key_token.as_str()))
.ok_or_else(|| {
Box::new(ERR::ErrorCustomSyntax(
format!("Invalid custom syntax prefix: {key_token}"),
custom.tokens.iter().map(<_>::to_string).collect(),
*pos,
))
})?;
let mut context = EvalContext::new(self, global, caches, scope, this_ptr);
(custom_def.func)(&mut context, &expressions, &custom.state)

View File

@ -111,8 +111,8 @@ impl GlobalRuntimeState {
tag: engine.default_tag().clone(),
#[cfg(feature = "debugging")]
debugger: engine.debugger.as_ref().map(|(init, ..)| {
crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init, init(engine))
debugger: engine.debugger.as_ref().map(|x| {
crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init, (x.0)(engine))
}),
}
}

View File

@ -502,7 +502,8 @@ impl Engine {
#[cfg(not(feature = "no_module"))]
let func = func.or_else(|| global.get_iter(iter_type)).or_else(|| {
self.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.find_map(|m| m.get_qualified_iter(iter_type))
});

View File

@ -212,7 +212,8 @@ impl Engine {
} else {
func.or_else(|| _global.get_qualified_fn(hash)).or_else(|| {
self.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id_raw())))
})
};
@ -252,7 +253,8 @@ impl Engine {
|| _global.may_contain_dynamic_fn(hash_base)
|| self
.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.any(|m| m.may_contain_dynamic_fn(hash_base));
// Set maximum bitmask when there are dynamic versions of the function

View File

@ -236,7 +236,7 @@ impl Engine {
// Then check imported modules
global.contains_qualified_fn(hash_script)
// Then check sub-modules
|| self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script));
|| self.global_sub_modules.iter().flat_map(|m| m.values()).any(|m| m.contains_qualified_fn(hash_script));
if !result && !cache.filter.is_absent_and_set(hash_script) {
// Do not cache "one-hit wonders"

View File

@ -179,7 +179,8 @@ fn has_native_fn_override(
#[cfg(not(feature = "no_module"))]
if engine
.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.any(|m| m.contains_qualified_fn(hash))
{
return true;

View File

@ -267,7 +267,8 @@ fn collect_fn_metadata(
#[cfg(not(feature = "no_module"))]
ctx.engine()
.global_sub_modules
.values()
.iter()
.flat_map(|m| m.values())
.flat_map(|m| m.iter_script_fn())
.filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f))
.for_each(|(.., f)| {

View File

@ -600,7 +600,10 @@ impl Engine {
&& index.is_none()
&& !is_global
&& !state.global_imports.iter().any(|m| m.as_str() == root)
&& !self.global_sub_modules.contains_key(root)
&& !self
.global_sub_modules
.as_ref()
.map_or(false, |m| m.contains_key(root))
{
return Err(
PERR::ModuleUndefined(root.into()).into_err(namespace.position())
@ -668,7 +671,10 @@ impl Engine {
&& index.is_none()
&& !is_global
&& !state.global_imports.iter().any(|m| m.as_str() == root)
&& !self.global_sub_modules.contains_key(root)
&& !self
.global_sub_modules
.as_ref()
.map_or(false, |m| m.contains_key(root))
{
return Err(
PERR::ModuleUndefined(root.into()).into_err(namespace.position())
@ -1550,9 +1556,16 @@ impl Engine {
// Custom syntax.
#[cfg(not(feature = "no_custom_syntax"))]
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key)
if !self.custom_syntax.is_empty() && self.custom_syntax.contains_key(&**key) =>
if self
.custom_syntax
.as_ref()
.map_or(false, |m| m.contains_key(&**key)) =>
{
let (key, syntax) = self.custom_syntax.get_key_value(&**key).unwrap();
let (key, syntax) = self
.custom_syntax
.as_ref()
.and_then(|m| m.get_key_value(&**key))
.unwrap();
let (.., pos) = input.next().expect(NEVER_ENDS);
let settings2 = settings.level_up()?;
self.parse_custom_syntax(input, state, lib, settings2, key, syntax, pos)?
@ -1856,7 +1869,10 @@ impl Engine {
&& index.is_none()
&& !is_global
&& !state.global_imports.iter().any(|m| m.as_str() == root)
&& !self.global_sub_modules.contains_key(root)
&& !self
.global_sub_modules
.as_ref()
.map_or(false, |m| m.contains_key(root))
{
return Err(
PERR::ModuleUndefined(root.into()).into_err(namespace.position())
@ -2297,7 +2313,8 @@ impl Engine {
#[cfg(not(feature = "no_custom_syntax"))]
Token::Custom(c) => self
.custom_keywords
.get(&**c)
.as_ref()
.and_then(|m| m.get(&**c))
.copied()
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?,
Token::Reserved(c) if !is_valid_identifier(c) => {
@ -2322,7 +2339,8 @@ impl Engine {
#[cfg(not(feature = "no_custom_syntax"))]
Token::Custom(c) => self
.custom_keywords
.get(&**c)
.as_ref()
.and_then(|m| m.get(&**c))
.copied()
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?,
Token::Reserved(c) if !is_valid_identifier(c) => {
@ -2414,7 +2432,8 @@ impl Engine {
Token::Custom(s)
if self
.custom_keywords
.get(s.as_str())
.as_ref()
.and_then(|m| m.get(s.as_str()))
.map_or(false, Option::is_some) =>
{
op_base.hashes = if is_valid_script_function {

View File

@ -170,7 +170,7 @@ pub fn gen_metadata_to_json(
let mut global = ModuleMetadata::new();
#[cfg(not(feature = "no_module"))]
for (name, m) in &engine.global_sub_modules {
for (name, m) in engine.global_sub_modules.iter().flat_map(|m| m.iter()) {
global.modules.insert(name, m.as_ref().into());
}

View File

@ -2435,7 +2435,7 @@ impl<'a> Iterator for TokenIterator<'a> {
Some((Token::Reserved(s), pos)) => (match
(s.as_str(),
#[cfg(not(feature = "no_custom_syntax"))]
self.engine.custom_keywords.contains_key(&*s),
self.engine.custom_keywords.as_ref().map_or(false, |m| m.contains_key(&*s)),
#[cfg(feature = "no_custom_syntax")]
false
)
@ -2472,7 +2472,7 @@ impl<'a> Iterator for TokenIterator<'a> {
#[cfg(feature = "no_custom_syntax")]
(.., true) => unreachable!("no custom operators"),
// Reserved keyword that is not custom and disabled.
(token, false) if self.engine.disabled_symbols.contains(token) => {
(token, false) if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token)) => {
let msg = format!("reserved {} '{token}' is disabled", if is_valid_identifier(token) { "keyword"} else {"symbol"});
Token::LexError(LERR::ImproperSymbol(s.to_string(), msg).into())
},
@ -2481,13 +2481,13 @@ impl<'a> Iterator for TokenIterator<'a> {
}, pos),
// Custom keyword
#[cfg(not(feature = "no_custom_syntax"))]
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.contains_key(&*s) => {
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(&*s)) => {
(Token::Custom(s), pos)
}
// Custom keyword/symbol - must be disabled
#[cfg(not(feature = "no_custom_syntax"))]
Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.contains_key(token.literal_syntax()) => {
if self.engine.disabled_symbols.contains(token.literal_syntax()) {
Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
// Disabled standard keyword/symbol
(Token::Custom(Box::new(token.literal_syntax().into())), pos)
} else {
@ -2496,7 +2496,7 @@ impl<'a> Iterator for TokenIterator<'a> {
}
}
// Disabled symbol
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.contains(token.literal_syntax()) => {
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => {
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
}
// Normal symbol