diff --git a/CHANGELOG.md b/CHANGELOG.md index bf9379c9..404692e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ Bug fixes --------- * In `Scope::clone_visible`, constants are now properly cloned as constants. +* Variables introduced inside `try` blocks are now properly cleaned up upon an exception. +* Off-by-one error in character positions after a comment line is now fixed. Script-breaking changes ----------------------- @@ -21,21 +23,12 @@ New features * A new bin tool, `rhai-dbg` (aka _The Rhai Debugger_), is added to showcase the debugging interface. * A new package, `DebuggingPackage`, is added which contains the `stack_trace` function to get the current call stack anywhere in a script. - -Version 1.4.2 -============= - -Bug fixes ---------- - -* Variables introduced inside `try` blocks are now properly cleaned up upon an exception. -* Off-by-one error in character positions after a comment line is now fixed. - Enhancements ------------ * `rhai-repl` tool has a few more commands, such as `strict` to turn on/off _Strict Variables Mode_ and `optimize` to turn on/off script optimization. * Default features for dependencies (such as `ahash/std` and `num-traits/std`) are no longer required. +* The `no_module` feature now eliminates large sections of code via feature gates. Version 1.4.1 diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index c35c49af..0411f3f0 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -72,7 +72,9 @@ impl Expression<'_> { #[must_use] pub fn get_string_value(&self) -> Option<&str> { match self.0 { - Expr::Variable(_, _, x) if x.1.is_none() => Some(x.2.as_str()), + #[cfg(not(feature = "no_module"))] + Expr::Variable(_, _, x) if x.1.is_some() => None, + Expr::Variable(_, _, x) => Some(x.2.as_str()), Expr::StringConstant(x, _) => Some(x.as_str()), _ => None, } diff --git a/src/api/register.rs b/src/api/register.rs index 717cc980..8dc9ce0c 100644 --- a/src/api/register.rs +++ b/src/api/register.rs @@ -1037,6 +1037,7 @@ impl Engine { signatures.extend(self.global_namespace().gen_fn_signatures()); + #[cfg(not(feature = "no_module"))] for (name, m) in &self.global_sub_modules { signatures.extend(m.gen_fn_signatures().map(|f| format!("{}::{}", name, f))) } diff --git a/src/ast/expr.rs b/src/ast/expr.rs index 8e60551e..1ec236d1 100644 --- a/src/ast/expr.rs +++ b/src/ast/expr.rs @@ -3,7 +3,6 @@ use super::{ASTNode, Ident, Stmt, StmtBlock}; use crate::engine::{KEYWORD_FN_PTR, OP_EXCLUSIVE_RANGE, OP_INCLUSIVE_RANGE}; use crate::func::hashing::ALT_ZERO_HASH; -use crate::module::Namespace; use crate::tokenizer::Token; use crate::types::dynamic::Union; use crate::{calc_fn_hash, Dynamic, FnPtr, Identifier, ImmutableString, Position, StaticVec, INT}; @@ -169,7 +168,10 @@ impl FnCallHashes { #[derive(Debug, Clone, Default, Hash)] pub struct FnCallExpr { /// Namespace of the function, if any. - pub namespace: Option, + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + pub namespace: Option, /// Function name. pub name: Identifier, /// Pre-calculated hashes. @@ -193,10 +195,15 @@ pub struct FnCallExpr { impl FnCallExpr { /// Does this function call contain a qualified namespace? + /// + /// Always `false` under `no_module`. #[inline(always)] #[must_use] pub const fn is_qualified(&self) -> bool { - self.namespace.is_some() + #[cfg(not(feature = "no_module"))] + return self.namespace.is_some(); + #[cfg(feature = "no_module")] + return false; } /// Convert this into an [`Expr::FnCall`]. #[inline(always)] @@ -368,7 +375,9 @@ pub enum Expr { Variable( Option, Position, - Box<(Option, Option<(Namespace, u64)>, Identifier)>, + #[cfg(not(feature = "no_module"))] + Box<(Option, Option<(crate::module::Namespace, u64)>, Identifier)>, + #[cfg(feature = "no_module")] Box<(Option, (), Identifier)>, ), /// Property access - ((getter, hash), (setter, hash), prop) Property( @@ -434,6 +443,8 @@ impl fmt::Debug for Expr { } Self::Variable(i, _, x) => { f.write_str("Variable(")?; + + #[cfg(not(feature = "no_module"))] if let Some((_, ref namespace)) = x.1 { write!(f, "{}{}", namespace, Token::DoubleColon.literal_syntax())? } @@ -451,6 +462,7 @@ impl fmt::Debug for Expr { } Self::FnCall(x, _) => { let mut ff = f.debug_struct("FnCall"); + #[cfg(not(feature = "no_module"))] x.namespace.as_ref().map(|ns| ff.field("namespace", ns)); ff.field("name", &x.name) .field("hash", &x.hashes) @@ -602,6 +614,7 @@ impl Expr { Union::FnPtr(f, _, _) if !f.is_curried() => Self::FnCall( FnCallExpr { + #[cfg(not(feature = "no_module"))] namespace: None, name: KEYWORD_FN_PTR.into(), hashes: calc_fn_hash(f.fn_name(), 1).into(), @@ -617,20 +630,32 @@ impl Expr { } } /// Is the expression a simple variable access? + /// + /// `non_qualified` is ignored under `no_module`. #[inline] #[must_use] pub(crate) const fn is_variable_access(&self, non_qualified: bool) -> bool { + let _non_qualified = non_qualified; + match self { - Self::Variable(_, _, x) => !non_qualified || x.1.is_none(), + #[cfg(not(feature = "no_module"))] + Self::Variable(_, _, x) if _non_qualified && x.1.is_some() => false, + Self::Variable(_, _, _) => true, _ => false, } } /// Return the variable name if the expression a simple variable access. + /// + /// `non_qualified` is ignored under `no_module`. #[inline] #[must_use] pub(crate) fn get_variable_name(&self, non_qualified: bool) -> Option<&str> { + let _non_qualified = non_qualified; + match self { - Self::Variable(_, _, x) if !non_qualified || x.1.is_none() => Some(x.2.as_str()), + #[cfg(not(feature = "no_module"))] + Self::Variable(_, _, x) if _non_qualified && x.1.is_some() => None, + Self::Variable(_, _, x) => Some(x.2.as_str()), _ => None, } } diff --git a/src/engine.rs b/src/engine.rs index bde0cbb8..14206d6f 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -96,6 +96,7 @@ pub struct Engine { /// A collection of all modules loaded into the global namespace of the Engine. pub(crate) global_modules: StaticVec>, /// A collection of all sub-modules directly loaded into the Engine. + #[cfg(not(feature = "no_module"))] pub(crate) global_sub_modules: BTreeMap>, /// A module resolution service. @@ -151,11 +152,11 @@ impl fmt::Debug for Engine { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("Engine"); - f.field("global_modules", &self.global_modules) - .field("global_sub_modules", &self.global_sub_modules); + f.field("global_modules", &self.global_modules); #[cfg(not(feature = "no_module"))] - f.field("module_resolver", &self.module_resolver.is_some()); + f.field("global_sub_modules", &self.global_sub_modules) + .field("module_resolver", &self.module_resolver.is_some()); f.field("type_names", &self.type_names) .field("disabled_symbols", &self.disabled_symbols) @@ -260,6 +261,8 @@ impl Engine { pub fn new_raw() -> Self { let mut engine = Self { global_modules: StaticVec::new_const(), + + #[cfg(not(feature = "no_module"))] global_sub_modules: BTreeMap::new(), #[cfg(not(feature = "no_module"))] diff --git a/src/eval/expr.rs b/src/eval/expr.rs index d81a7755..6b8a1287 100644 --- a/src/eval/expr.rs +++ b/src/eval/expr.rs @@ -3,25 +3,23 @@ use super::{EvalContext, EvalState, GlobalRuntimeState, Target}; use crate::ast::{Expr, FnCallExpr, OpAssignment}; use crate::engine::{KEYWORD_THIS, OP_CONCAT}; -use crate::module::Namespace; use crate::types::dynamic::AccessMode; -use crate::{ - Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, Shared, StaticVec, ERR, -}; +use crate::{Dynamic, Engine, Module, Position, RhaiResult, RhaiResultOf, Scope, StaticVec, ERR}; use std::num::NonZeroUsize; #[cfg(feature = "no_std")] use std::prelude::v1::*; impl Engine { /// Search for a module within an imports stack. + #[cfg(not(feature = "no_module"))] #[inline] #[must_use] pub(crate) fn search_imports( &self, global: &GlobalRuntimeState, state: &mut EvalState, - namespace: &Namespace, - ) -> Option> { + namespace: &crate::module::Namespace, + ) -> Option> { let root = &namespace[0].name; // Qualified - check if the root module is directly indexed @@ -59,7 +57,10 @@ impl Engine { } Expr::Variable(None, _var_pos, v) => match v.as_ref() { // Normal variable access + #[cfg(not(feature = "no_module"))] (_, None, _) => self.search_scope_only(scope, global, state, lib, this_ptr, expr), + #[cfg(feature = "no_module")] + (_, (), _) => self.search_scope_only(scope, global, state, lib, this_ptr, expr), // Qualified variable access #[cfg(not(feature = "no_module"))] @@ -117,9 +118,6 @@ impl Engine { Err(ERR::ErrorModuleNotFound(namespace.to_string(), namespace[0].pos).into()) } - - #[cfg(feature = "no_module")] - (_, Some((_, _)), _) => unreachable!("qualified access under no_module"), }, _ => unreachable!("Expr::Variable expected but gets {:?}", expr), } @@ -211,6 +209,7 @@ impl Engine { ) -> RhaiResult { let FnCallExpr { name, + #[cfg(not(feature = "no_module"))] namespace, capture_parent_scope: capture, hashes, @@ -219,26 +218,27 @@ impl Engine { .. } = expr; + #[cfg(not(feature = "no_module"))] if let Some(namespace) = namespace.as_ref() { // Qualified function call let hash = hashes.native; - self.make_qualified_function_call( + return self.make_qualified_function_call( scope, global, state, lib, this_ptr, namespace, name, args, constants, hash, pos, level, - ) - } else { - // Normal function call - let (first_arg, args) = args.split_first().map_or_else( - || (None, args.as_ref()), - |(first, rest)| (Some(first), rest), ); - - self.make_function_call( - scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, - pos, *capture, level, - ) } + + // Normal function call + let (first_arg, args) = args.split_first().map_or_else( + || (None, args.as_ref()), + |(first, rest)| (Some(first), rest), + ); + + self.make_function_call( + scope, global, state, lib, this_ptr, name, first_arg, args, constants, *hashes, pos, + *capture, level, + ) } /// Evaluate an expression. diff --git a/src/eval/global_state.rs b/src/eval/global_state.rs index 94b730c9..fa7979b7 100644 --- a/src/eval/global_state.rs +++ b/src/eval/global_state.rs @@ -1,30 +1,30 @@ //! Global runtime state. -use crate::func::{CallableFunction, IteratorFn}; -use crate::{Engine, Identifier, Module, Shared, StaticVec}; +use crate::{Engine, Identifier}; #[cfg(feature = "no_std")] use std::prelude::v1::*; -use std::{ - any::TypeId, - fmt, - iter::{Rev, Zip}, - marker::PhantomData, -}; +use std::{fmt, marker::PhantomData}; -/// _(internals)_ A stack of imported [modules][Module] plus mutable global runtime states. +/// _(internals)_ Global runtime states. /// Exported under the `internals` feature only. // // # Implementation Notes // -// This implementation splits the module names from the shared modules to improve data locality. -// Most usage will be looking up a particular key from the list and then getting the module that -// corresponds to that key. +// This implementation for imported [modules][crate::Module] splits the module names from the shared +// modules to improve data locality. Most usage will be looking up a particular key from the list +// and then getting the module that corresponds to that key. #[derive(Clone)] pub struct GlobalRuntimeState<'a> { /// Stack of module names. - keys: StaticVec, - /// Stack of imported [modules][Module]. - modules: StaticVec>, + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + keys: crate::StaticVec, + /// Stack of imported [modules][crate::Module]. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] + modules: crate::StaticVec>, /// Source of the current context. /// No source if the string is empty. pub source: Identifier, @@ -35,9 +35,10 @@ pub struct GlobalRuntimeState<'a> { /// Function call hashes to index getters and setters. #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] fn_hash_indexing: (u64, u64), - /// Embedded [module][Module] resolver. + /// Embedded [crate::Module][crate::Module] resolver. #[cfg(not(feature = "no_module"))] - pub embedded_module_resolver: Option>, + pub embedded_module_resolver: + Option>, /// Cache of globally-defined constants. /// /// Interior mutability is needed because it is shared in order to aid in cloning. @@ -61,8 +62,10 @@ impl GlobalRuntimeState<'_> { let _engine = engine; Self { - keys: StaticVec::new_const(), - modules: StaticVec::new_const(), + #[cfg(not(feature = "no_module"))] + keys: crate::StaticVec::new_const(), + #[cfg(not(feature = "no_module"))] + modules: crate::StaticVec::new_const(), source: Identifier::new_const(), num_operations: 0, num_modules_loaded: 0, @@ -78,26 +81,41 @@ impl GlobalRuntimeState<'_> { dummy: PhantomData::default(), } } - /// Get the length of the stack of globally-imported [modules][Module]. + /// Get the length of the stack of globally-imported [modules][crate::Module]. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] pub fn num_imports(&self) -> usize { self.keys.len() } - /// Get the globally-imported [module][Module] at a particular index. + /// Get the globally-imported [crate::Module][crate::Module] at a particular index. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] - pub fn get_shared_import(&self, index: usize) -> Option> { + pub fn get_shared_import(&self, index: usize) -> Option> { self.modules.get(index).cloned() } - /// Get a mutable reference to the globally-imported [module][Module] at a particular index. + /// Get a mutable reference to the globally-imported [crate::Module][crate::Module] at a particular index. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline(always)] #[must_use] - pub(crate) fn get_shared_import_mut(&mut self, index: usize) -> Option<&mut Shared> { + pub(crate) fn get_shared_import_mut( + &mut self, + index: usize, + ) -> Option<&mut crate::Shared> { self.modules.get_mut(index) } - /// Get the index of a globally-imported [module][Module] by name. + /// Get the index of a globally-imported [crate::Module][crate::Module] by name. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline] #[must_use] pub fn find_import(&self, name: &str) -> Option { @@ -111,69 +129,107 @@ impl GlobalRuntimeState<'_> { } }) } - /// Push an imported [module][Module] onto the stack. + /// Push an imported [crate::Module][crate::Module] onto the stack. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline(always)] - pub fn push_import(&mut self, name: impl Into, module: impl Into>) { + pub fn push_import( + &mut self, + name: impl Into, + module: impl Into>, + ) { self.keys.push(name.into()); self.modules.push(module.into()); } - /// Truncate the stack of globally-imported [modules][Module] to a particular length. + /// Truncate the stack of globally-imported [modules][crate::Module] to a particular length. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline(always)] pub fn truncate_imports(&mut self, size: usize) { self.keys.truncate(size); self.modules.truncate(size); } - /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. + /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline] - pub fn iter_imports(&self) -> impl Iterator { + pub fn iter_imports(&self) -> impl Iterator { self.keys .iter() .rev() .zip(self.modules.iter().rev()) .map(|(name, module)| (name.as_str(), module.as_ref())) } - /// Get an iterator to the stack of globally-imported [modules][Module] in reverse order. + /// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline] - pub(crate) fn iter_imports_raw(&self) -> impl Iterator)> { + pub(crate) fn iter_imports_raw( + &self, + ) -> impl Iterator)> { self.keys.iter().rev().zip(self.modules.iter().rev()) } - /// Get an iterator to the stack of globally-imported [modules][Module] in forward order. + /// Get an iterator to the stack of globally-imported [modules][crate::Module] in forward order. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline] - pub fn scan_imports_raw(&self) -> impl Iterator)> { + pub fn scan_imports_raw( + &self, + ) -> impl Iterator)> { self.keys.iter().zip(self.modules.iter()) } - /// Does the specified function hash key exist in the stack of globally-imported [modules][Module]? + /// Does the specified function hash key exist in the stack of globally-imported [modules][crate::Module]? + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline] #[must_use] pub fn contains_qualified_fn(&self, hash: u64) -> bool { self.modules.iter().any(|m| m.contains_qualified_fn(hash)) } - /// Get the specified function via its hash key from the stack of globally-imported [modules][Module]. + /// Get the specified function via its hash key from the stack of globally-imported [modules][crate::Module]. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline] #[must_use] - pub fn get_qualified_fn(&self, hash: u64) -> Option<(&CallableFunction, Option<&str>)> { + pub fn get_qualified_fn( + &self, + hash: u64, + ) -> Option<(&crate::func::CallableFunction, Option<&str>)> { self.modules .iter() .rev() .find_map(|m| m.get_qualified_fn(hash).map(|f| (f, m.id()))) } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in the stack of - /// globally-imported [modules][Module]? + /// globally-imported [modules][crate::Module]? + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[allow(dead_code)] #[inline] #[must_use] - pub fn contains_iter(&self, id: TypeId) -> bool { + pub fn contains_iter(&self, id: std::any::TypeId) -> bool { self.modules.iter().any(|m| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator from the stack of globally-imported - /// [modules][Module]. + /// [modules][crate::Module]. + /// + /// Not available under `no_module`. + #[cfg(not(feature = "no_module"))] #[inline] #[must_use] - pub fn get_iter(&self, id: TypeId) -> Option<&IteratorFn> { + pub fn get_iter(&self, id: std::any::TypeId) -> Option<&crate::func::IteratorFn> { self.modules .iter() .rev() @@ -216,10 +272,13 @@ impl GlobalRuntimeState<'_> { } } +#[cfg(not(feature = "no_module"))] impl IntoIterator for GlobalRuntimeState<'_> { - type Item = (Identifier, Shared); - type IntoIter = - Zip>, Rev; 3]>>>; + type Item = (Identifier, crate::Shared); + type IntoIter = std::iter::Zip< + std::iter::Rev>, + std::iter::Rev; 3]>>, + >; #[inline] fn into_iter(self) -> Self::IntoIter { @@ -230,7 +289,10 @@ impl IntoIterator for GlobalRuntimeState<'_> { } } -impl, M: Into>> Extend<(K, M)> for GlobalRuntimeState<'_> { +#[cfg(not(feature = "no_module"))] +impl, M: Into>> Extend<(K, M)> + for GlobalRuntimeState<'_> +{ #[inline] fn extend>(&mut self, iter: T) { for (k, m) in iter { @@ -245,8 +307,10 @@ impl fmt::Debug for GlobalRuntimeState<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { let mut f = f.debug_struct("GlobalRuntimeState"); - f.field("imports", &self.keys.iter().zip(self.modules.iter())) - .field("source", &self.source) + #[cfg(not(feature = "no_module"))] + f.field("imports", &self.keys.iter().zip(self.modules.iter())); + + f.field("source", &self.source) .field("num_operations", &self.num_operations) .field("num_modules_loaded", &self.num_modules_loaded); diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index df8894b4..0ccfbd6f 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -37,6 +37,7 @@ impl Engine { let orig_always_search_scope = state.always_search_scope; let orig_scope_len = scope.len(); + #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); let orig_fn_resolution_caches_len = state.fn_resolution_caches_len(); @@ -96,6 +97,7 @@ impl Engine { if restore_orig_state { scope.rewind(orig_scope_len); state.scope_level -= 1; + #[cfg(not(feature = "no_module"))] global.truncate_imports(orig_imports_len); // The impact of new local variables goes away at the end of a block @@ -579,11 +581,20 @@ impl Engine { .global_modules .iter() .find_map(|m| m.get_iter(iter_type)) - .or_else(|| global.get_iter(iter_type)) .or_else(|| { - self.global_sub_modules + #[cfg(not(feature = "no_module"))] + return global.get_iter(iter_type); + #[cfg(feature = "no_module")] + return None; + }) + .or_else(|| { + #[cfg(not(feature = "no_module"))] + return self + .global_sub_modules .values() - .find_map(|m| m.get_qualified_iter(iter_type)) + .find_map(|m| m.get_qualified_iter(iter_type)); + #[cfg(feature = "no_module")] + return None; }); if let Some(func) = func { diff --git a/src/func/call.rs b/src/func/call.rs index 35e8690d..5b57e0bb 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -10,8 +10,6 @@ use crate::engine::{ KEYWORD_IS_DEF_VAR, KEYWORD_PRINT, KEYWORD_TYPE_OF, }; use crate::eval::{EvalState, GlobalRuntimeState}; -use crate::module::Namespace; -use crate::tokenizer::Token; use crate::{ calc_fn_hash, calc_fn_params_hash, combine_hashes, Dynamic, Engine, FnArgsVec, FnPtr, Identifier, ImmutableString, Module, Position, RhaiResult, RhaiResultOf, Scope, ERR, @@ -150,18 +148,26 @@ impl Engine { #[must_use] fn gen_call_signature( &self, - namespace: Option<&Namespace>, + #[cfg(not(feature = "no_module"))] namespace: Option<&crate::module::Namespace>, fn_name: &str, args: &[&mut Dynamic], ) -> String { - format!( - "{}{}{} ({})", + #[cfg(not(feature = "no_module"))] + let (ns, sep) = ( namespace.map_or_else(|| String::new(), |ns| ns.to_string()), if namespace.is_some() { - Token::DoubleColon.literal_syntax() + crate::tokenizer::Token::DoubleColon.literal_syntax() } else { "" }, + ); + #[cfg(feature = "no_module")] + let (ns, sep) = ("", ""); + + format!( + "{}{}{} ({})", + ns, + sep, fn_name, args.iter() .map(|a| if a.is::() { @@ -194,6 +200,8 @@ impl Engine { allow_dynamic: bool, is_op_assignment: bool, ) -> Option<&'s FnResolutionCacheEntry> { + let _global = global; + if hash_script == 0 { return None; } @@ -235,23 +243,29 @@ impl Engine { }) }) .or_else(|| { - global.get_qualified_fn(hash).map(|(func, source)| { + #[cfg(not(feature = "no_module"))] + return _global.get_qualified_fn(hash).map(|(func, source)| { FnResolutionCacheEntry { func: func.clone(), source: source .map_or_else(|| Identifier::new_const(), Into::into), } - }) + }); + #[cfg(feature = "no_module")] + return None; }) .or_else(|| { - self.global_sub_modules.values().find_map(|m| { + #[cfg(not(feature = "no_module"))] + return self.global_sub_modules.values().find_map(|m| { m.get_qualified_fn(hash).cloned().map(|func| { FnResolutionCacheEntry { func, source: m.id_raw().clone(), } }) - }) + }); + #[cfg(feature = "no_module")] + return None; }); match func { @@ -506,9 +520,16 @@ impl Engine { } // Raise error - _ => Err( - ERR::ErrorFunctionNotFound(self.gen_call_signature(None, name, args), pos).into(), - ), + _ => Err(ERR::ErrorFunctionNotFound( + self.gen_call_signature( + #[cfg(not(feature = "no_module"))] + None, + name, + args, + ), + pos, + ) + .into()), } } @@ -1196,6 +1217,7 @@ impl Engine { } /// Call a namespace-qualified function in normal function-call style. + #[cfg(not(feature = "no_module"))] pub(crate) fn make_qualified_function_call( &self, scope: &mut Scope, @@ -1203,7 +1225,7 @@ impl Engine { state: &mut EvalState, lib: &[&Module], this_ptr: &mut Option<&mut Dynamic>, - namespace: &Namespace, + namespace: &crate::module::Namespace, fn_name: &str, args_expr: &[Expr], constants: &[Dynamic], diff --git a/src/func/native.rs b/src/func/native.rs index 1d85a9fc..7938f4cd 100644 --- a/src/func/native.rs +++ b/src/func/native.rs @@ -215,7 +215,6 @@ impl<'a> NativeCallContext<'a> { /// /// Not available under `no_module`. #[cfg(feature = "internals")] - #[cfg(not(feature = "no_module"))] #[inline(always)] #[must_use] pub const fn global_runtime_state(&self) -> Option<&GlobalRuntimeState> { diff --git a/src/func/script.rs b/src/func/script.rs index 42a5caba..c087053e 100644 --- a/src/func/script.rs +++ b/src/func/script.rs @@ -70,6 +70,7 @@ impl Engine { } let orig_scope_len = scope.len(); + #[cfg(not(feature = "no_module"))] let orig_imports_len = global.num_imports(); #[cfg(feature = "debugging")] @@ -161,6 +162,7 @@ impl Engine { // Remove arguments only, leaving new variables in the scope scope.remove_range(orig_scope_len, args.len()) } + #[cfg(not(feature = "no_module"))] global.truncate_imports(orig_imports_len); // Restore state @@ -183,6 +185,8 @@ impl Engine { lib: &[&Module], hash_script: u64, ) -> bool { + let _global = global; + let cache = state.fn_resolution_cache_mut(); if let Some(result) = cache.get(&hash_script).map(|v| v.is_some()) { @@ -192,9 +196,12 @@ impl Engine { // First check script-defined functions let result = lib.iter().any(|&m| m.contains_fn(hash_script)) // Then check the global namespace and packages - || self.global_modules.iter().any(|m| m.contains_fn(hash_script)) + || self.global_modules.iter().any(|m| m.contains_fn(hash_script)); + + #[cfg(not(feature = "no_module"))] + let result = result || // Then check imported modules - || global.map_or(false, |m| m.contains_qualified_fn(hash_script)) + _global.map_or(false, |m| m.contains_qualified_fn(hash_script)) // Then check sub-modules || self.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash_script)); diff --git a/src/lib.rs b/src/lib.rs index 58c79df0..3e514dd8 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -270,6 +270,7 @@ pub use eval::{EvalState, GlobalRuntimeState}; pub use func::call::{FnResolutionCache, FnResolutionCacheEntry}; #[cfg(feature = "internals")] +#[cfg(not(feature = "no_module"))] pub use module::Namespace; /// Alias to [`smallvec::SmallVec<[T; 3]>`](https://crates.io/crates/smallvec), which is a diff --git a/src/module/mod.rs b/src/module/mod.rs index 26049a1f..d85a3fb0 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1,11 +1,10 @@ //! Module defining external-loaded modules for Rhai. -use crate::ast::{FnAccess, Ident}; +use crate::ast::FnAccess; use crate::func::{ shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction, SendSync, }; -use crate::tokenizer::Token; use crate::types::dynamic::Variant; use crate::{ calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier, @@ -19,8 +18,7 @@ use std::{ collections::{BTreeMap, BTreeSet}, fmt, iter::{empty, once}, - num::NonZeroUsize, - ops::{Add, AddAssign, Deref, DerefMut}, + ops::{Add, AddAssign}, }; /// A type representing the namespace of a function. @@ -29,6 +27,8 @@ pub enum FnNamespace { /// Expose to global namespace. Global, /// Module namespace only. + /// + /// Ignored under `no_module`. Internal, } @@ -1372,6 +1372,7 @@ impl Module { /// Get a namespace-qualified function. /// /// The [`u64`] hash is calculated by [`build_index`][Module::build_index]. + #[cfg(not(feature = "no_module"))] #[inline] #[must_use] pub(crate) fn get_qualified_fn(&self, hash_qualified_fn: u64) -> Option<&CallableFunction> { @@ -1960,6 +1961,7 @@ impl Module { } /// Get the specified type iterator. + #[cfg(not(feature = "no_module"))] #[inline] #[must_use] pub(crate) fn get_qualified_iter(&self, id: TypeId) -> Option<&IteratorFn> { @@ -1974,112 +1976,14 @@ impl Module { } } -/// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function call. -/// Exported under the `internals` feature only. -/// -/// A [`u64`] offset to the current [stack of imported modules][crate::GlobalRuntimeState] is -/// cached for quick search purposes. -/// -/// A [`StaticVec`] is used because the vast majority of namespace-qualified access contains only -/// one level, and it is wasteful to always allocate a [`Vec`] with one element. -#[derive(Clone, Eq, PartialEq, Default, Hash)] -pub struct Namespace { - index: Option, - path: StaticVec, -} - -impl fmt::Debug for Namespace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - if let Some(index) = self.index { - write!(f, "{} -> ", index)?; - } - - f.write_str( - &self - .path - .iter() - .map(|Ident { name, .. }| name.as_str()) - .collect::>() - .join(Token::DoubleColon.literal_syntax()), - ) - } -} - -impl fmt::Display for Namespace { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - f.write_str( - &self - .path - .iter() - .map(|Ident { name, .. }| name.as_str()) - .collect::>() - .join(Token::DoubleColon.literal_syntax()), - ) - } -} - -impl Deref for Namespace { - type Target = StaticVec; - - #[inline(always)] - fn deref(&self) -> &Self::Target { - &self.path - } -} - -impl DerefMut for Namespace { - #[inline(always)] - fn deref_mut(&mut self) -> &mut Self::Target { - &mut self.path - } -} - -impl From> for Namespace { - #[inline(always)] - fn from(mut path: Vec) -> Self { - path.shrink_to_fit(); - Self { - index: None, - path: path.into(), - } - } -} - -impl From> for Namespace { - #[inline(always)] - fn from(mut path: StaticVec) -> Self { - path.shrink_to_fit(); - Self { index: None, path } - } -} - -impl Namespace { - /// Create a new [`Namespace`]. - #[inline(always)] - #[must_use] - pub const fn new() -> Self { - Self { - index: None, - path: StaticVec::new_const(), - } - } - /// Get the [`Scope`][crate::Scope] index offset. - #[inline(always)] - #[must_use] - pub(crate) const fn index(&self) -> Option { - self.index - } - /// Set the [`Scope`][crate::Scope] index offset. - #[cfg(not(feature = "no_module"))] - #[inline(always)] - pub(crate) fn set_index(&mut self, index: Option) { - self.index = index - } -} - -#[cfg(not(feature = "no_module"))] -pub use resolvers::ModuleResolver; +mod namespace; /// Module containing all built-in [module resolvers][ModuleResolver]. #[cfg(not(feature = "no_module"))] pub mod resolvers; + +#[cfg(not(feature = "no_module"))] +pub use namespace::Namespace; + +#[cfg(not(feature = "no_module"))] +pub use resolvers::ModuleResolver; diff --git a/src/module/namespace.rs b/src/module/namespace.rs new file mode 100644 index 00000000..abfb9901 --- /dev/null +++ b/src/module/namespace.rs @@ -0,0 +1,117 @@ +//! Namespace reference type. +#![cfg(not(feature = "no_module"))] + +use crate::ast::Ident; +use crate::tokenizer::Token; +use crate::StaticVec; +#[cfg(feature = "no_std")] +use std::prelude::v1::*; +use std::{ + fmt, + num::NonZeroUsize, + ops::{Deref, DerefMut}, +}; + +/// _(internals)_ A chain of [module][Module] names to namespace-qualify a variable or function call. +/// Exported under the `internals` feature only. +/// +/// Not available under `no_module`. +/// +/// A [`u64`] offset to the current [stack of imported modules][crate::GlobalRuntimeState] is +/// cached for quick search purposes. +/// +/// A [`StaticVec`] is used because the vast majority of namespace-qualified access contains only +/// one level, and it is wasteful to always allocate a [`Vec`] with one element. +#[derive(Clone, Eq, PartialEq, Default, Hash)] +pub struct Namespace { + index: Option, + path: StaticVec, +} + +impl fmt::Debug for Namespace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + if let Some(index) = self.index { + write!(f, "{} -> ", index)?; + } + + f.write_str( + &self + .path + .iter() + .map(|Ident { name, .. }| name.as_str()) + .collect::>() + .join(Token::DoubleColon.literal_syntax()), + ) + } +} + +impl fmt::Display for Namespace { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.write_str( + &self + .path + .iter() + .map(|Ident { name, .. }| name.as_str()) + .collect::>() + .join(Token::DoubleColon.literal_syntax()), + ) + } +} + +impl Deref for Namespace { + type Target = StaticVec; + + #[inline(always)] + fn deref(&self) -> &Self::Target { + &self.path + } +} + +impl DerefMut for Namespace { + #[inline(always)] + fn deref_mut(&mut self) -> &mut Self::Target { + &mut self.path + } +} + +impl From> for Namespace { + #[inline(always)] + fn from(mut path: Vec) -> Self { + path.shrink_to_fit(); + Self { + index: None, + path: path.into(), + } + } +} + +impl From> for Namespace { + #[inline(always)] + fn from(mut path: StaticVec) -> Self { + path.shrink_to_fit(); + Self { index: None, path } + } +} + +impl Namespace { + /// Create a new [`Namespace`]. + #[inline(always)] + #[must_use] + pub const fn new() -> Self { + Self { + index: None, + path: StaticVec::new_const(), + } + } + /// Get the [`Scope`][crate::Scope] index offset. + #[inline(always)] + #[must_use] + pub(crate) const fn index(&self) -> Option { + self.index + } + /// Set the [`Scope`][crate::Scope] index offset. + #[inline(always)] + pub(crate) fn set_index(&mut self, index: Option) { + self.index = index + } +} diff --git a/src/optimizer.rs b/src/optimizer.rs index 5e4ec27d..7e2cb6e7 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -164,9 +164,21 @@ fn has_native_fn_override( // First check the global namespace and packages, but skip modules that are standard because // they should never conflict with system functions. - engine.global_modules.iter().filter(|m| !m.standard).any(|m| m.contains_fn(hash)) - // Then check sub-modules - || engine.global_sub_modules.values().any(|m| m.contains_qualified_fn(hash)) + let result = engine + .global_modules + .iter() + .filter(|m| !m.standard) + .any(|m| m.contains_fn(hash)); + + #[cfg(not(feature = "no_module"))] + // Then check sub-modules + let result = result + || engine + .global_sub_modules + .values() + .any(|m| m.contains_qualified_fn(hash)); + + result } /// Optimize a block of [statements][Stmt]. @@ -1113,7 +1125,9 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, chaining: bool) { }, // constant-name - Expr::Variable(_, pos, x) if x.1.is_none() && state.find_constant(&x.2).is_some() => { + #[cfg(not(feature = "no_module"))] + Expr::Variable(_, _, x) if x.1.is_some() => (), + Expr::Variable(_, pos, x) if state.find_constant(&x.2).is_some() => { // Replace constant with value *expr = Expr::from_dynamic(state.find_constant(&x.2).unwrap().clone(), *pos); state.set_dirty(); diff --git a/src/packages/debugging.rs b/src/packages/debugging.rs index d438b889..397f8b8e 100644 --- a/src/packages/debugging.rs +++ b/src/packages/debugging.rs @@ -72,7 +72,7 @@ mod debugging_functions { Dynamic::from_map(map) } #[cfg(feature = "no_object")] - call.into() + display.into() }, ) .collect() diff --git a/src/packages/lang_core.rs b/src/packages/lang_core.rs index 9e5073d7..cbe6db9c 100644 --- a/src/packages/lang_core.rs +++ b/src/packages/lang_core.rs @@ -92,13 +92,14 @@ fn collect_fn_metadata( // Create a metadata record for a function. fn make_metadata( dict: &BTreeSet, - namespace: Option, + #[cfg(not(feature = "no_module"))] namespace: Option, func: &ScriptFnDef, ) -> Map { const DICT: &str = "key exists"; let mut map = Map::new(); + #[cfg(not(feature = "no_module"))] if let Some(ns) = namespace { map.insert(dict.get("namespace").expect(DICT).clone(), ns.into()); } @@ -133,6 +134,7 @@ fn collect_fn_metadata( // Intern strings let dict: BTreeSet = [ + #[cfg(not(feature = "no_module"))] "namespace", "name", "access", @@ -150,21 +152,52 @@ fn collect_fn_metadata( ctx.iter_namespaces() .flat_map(Module::iter_script_fn) .filter(|(s, a, n, p, f)| filter(*s, *a, n, *p, f)) - .for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into())); + .for_each(|(_, _, _, _, f)| { + list.push( + make_metadata( + &dict, + #[cfg(not(feature = "no_module"))] + None, + f, + ) + .into(), + ) + }); ctx.engine() .global_modules .iter() .flat_map(|m| m.iter_script_fn()) .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f)) - .for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into())); + .for_each(|(_, _, _, _, f)| { + list.push( + make_metadata( + &dict, + #[cfg(not(feature = "no_module"))] + None, + f, + ) + .into(), + ) + }); + #[cfg(not(feature = "no_module"))] ctx.engine() .global_sub_modules .values() .flat_map(|m| m.iter_script_fn()) .filter(|(ns, a, n, p, f)| filter(*ns, *a, n, *p, f)) - .for_each(|(_, _, _, _, f)| list.push(make_metadata(&dict, None, f).into())); + .for_each(|(_, _, _, _, f)| { + list.push( + make_metadata( + &dict, + #[cfg(not(feature = "no_module"))] + None, + f, + ) + .into(), + ) + }); #[cfg(not(feature = "no_module"))] { diff --git a/src/parser.rs b/src/parser.rs index 80d98e3a..21d12147 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -8,7 +8,6 @@ use crate::ast::{ }; use crate::engine::{Precedence, KEYWORD_THIS, OP_CONTAINS}; use crate::func::hashing::get_hasher; -use crate::module::Namespace; use crate::tokenizer::{ is_keyword_function, is_valid_function_name, is_valid_identifier, Token, TokenStream, TokenizerControl, @@ -16,9 +15,8 @@ use crate::tokenizer::{ use crate::types::dynamic::AccessMode; use crate::types::StringsInterner; use crate::{ - calc_fn_hash, calc_qualified_fn_hash, calc_qualified_var_hash, Dynamic, Engine, ExclusiveRange, - Identifier, ImmutableString, InclusiveRange, LexError, ParseError, Position, Scope, Shared, - StaticVec, AST, INT, PERR, + calc_fn_hash, Dynamic, Engine, ExclusiveRange, Identifier, ImmutableString, InclusiveRange, + LexError, ParseError, Position, Scope, Shared, StaticVec, AST, INT, PERR, }; #[cfg(feature = "no_std")] use std::prelude::v1::*; @@ -266,7 +264,9 @@ impl Expr { #[must_use] fn into_property(self, state: &mut ParseState) -> Self { match self { - Self::Variable(_, pos, x) if x.1.is_none() => { + #[cfg(not(feature = "no_module"))] + Self::Variable(_, _, ref x) if x.1.is_some() => self, + Self::Variable(_, pos, x) => { let ident = x.2; let getter = state.get_identifier(crate::engine::FN_GET, &ident); let hash_get = calc_fn_hash(&getter, 1); @@ -452,7 +452,7 @@ fn parse_fn_call( lib: &mut FnLib, id: Identifier, capture_parent_scope: bool, - namespace: Option, + #[cfg(not(feature = "no_module"))] namespace: Option, settings: ParseSettings, ) -> ParseResult { #[cfg(not(feature = "unchecked"))] @@ -460,6 +460,7 @@ fn parse_fn_call( let (token, token_pos) = input.peek().expect(NEVER_ENDS); + #[cfg(not(feature = "no_module"))] let mut namespace = namespace; let mut args = StaticVec::new_const(); @@ -478,28 +479,29 @@ fn parse_fn_call( Token::RightParen => { eat_token(input, Token::RightParen); + #[cfg(not(feature = "no_module"))] let hash = if let Some(modules) = namespace.as_mut() { - #[cfg(not(feature = "no_module"))] - { - let index = state.find_module(&modules[0].name); + let index = state.find_module(&modules[0].name); - #[cfg(not(feature = "no_function"))] - let relax = settings.is_function_scope; - #[cfg(feature = "no_function")] - let relax = false; + #[cfg(not(feature = "no_function"))] + let relax = settings.is_function_scope; + #[cfg(feature = "no_function")] + let relax = false; - if !relax && settings.strict_var && index.is_none() { - return Err(PERR::ModuleUndefined(modules[0].name.to_string()) - .into_err(modules[0].pos)); - } - - modules.set_index(index); + if !relax && settings.strict_var && index.is_none() { + return Err( + PERR::ModuleUndefined(modules[0].name.to_string()).into_err(modules[0].pos) + ); } - calc_qualified_fn_hash(modules.iter().map(|m| m.name.as_str()), &id, 0) + modules.set_index(index); + + crate::calc_qualified_fn_hash(modules.iter().map(|m| m.name.as_str()), &id, 0) } else { calc_fn_hash(&id, 0) }; + #[cfg(feature = "no_module")] + let hash = calc_fn_hash(&id, 0); let hashes = if is_valid_function_name(&id) { hash.into() @@ -512,6 +514,7 @@ fn parse_fn_call( return Ok(FnCallExpr { name: state.get_identifier("", id), capture_parent_scope, + #[cfg(not(feature = "no_module"))] namespace, hashes, args, @@ -537,28 +540,32 @@ fn parse_fn_call( (Token::RightParen, _) => { eat_token(input, Token::RightParen); + #[cfg(not(feature = "no_module"))] let hash = if let Some(modules) = namespace.as_mut() { - #[cfg(not(feature = "no_module"))] - { - let index = state.find_module(&modules[0].name); + let index = state.find_module(&modules[0].name); - #[cfg(not(feature = "no_function"))] - let relax = settings.is_function_scope; - #[cfg(feature = "no_function")] - let relax = false; + #[cfg(not(feature = "no_function"))] + let relax = settings.is_function_scope; + #[cfg(feature = "no_function")] + let relax = false; - if !relax && settings.strict_var && index.is_none() { - return Err(PERR::ModuleUndefined(modules[0].name.to_string()) - .into_err(modules[0].pos)); - } - - modules.set_index(index); + if !relax && settings.strict_var && index.is_none() { + return Err(PERR::ModuleUndefined(modules[0].name.to_string()) + .into_err(modules[0].pos)); } - calc_qualified_fn_hash(modules.iter().map(|m| m.name.as_str()), &id, args.len()) + modules.set_index(index); + + crate::calc_qualified_fn_hash( + modules.iter().map(|m| m.name.as_str()), + &id, + args.len(), + ) } else { calc_fn_hash(&id, args.len()) }; + #[cfg(feature = "no_module")] + let hash = calc_fn_hash(&id, args.len()); let hashes = if is_valid_function_name(&id) { hash.into() @@ -571,6 +578,7 @@ fn parse_fn_call( return Ok(FnCallExpr { name: state.get_identifier("", id), capture_parent_scope, + #[cfg(not(feature = "no_module"))] namespace, hashes, args, @@ -1338,6 +1346,11 @@ fn parse_primary( // Identifier Token::Identifier(_) => { + #[cfg(not(feature = "no_module"))] + let none = None; + #[cfg(feature = "no_module")] + let none = (); + let s = match input.next().expect(NEVER_ENDS) { (Token::Identifier(s), _) => s, token => unreachable!("Token::Identifier expected but gets {:?}", token), @@ -1354,7 +1367,7 @@ fn parse_primary( Expr::Variable( None, settings.pos, - (None, None, state.get_identifier("", s)).into(), + (None, none, state.get_identifier("", s)).into(), ) } // Namespace qualification @@ -1368,7 +1381,7 @@ fn parse_primary( Expr::Variable( None, settings.pos, - (None, None, state.get_identifier("", s)).into(), + (None, none, state.get_identifier("", s)).into(), ) } // Normal variable access @@ -1389,7 +1402,7 @@ fn parse_primary( Expr::Variable( short_index, settings.pos, - (index, None, state.get_identifier("", s)).into(), + (index, none, state.get_identifier("", s)).into(), ) } } @@ -1397,6 +1410,11 @@ fn parse_primary( // Reserved keyword or symbol Token::Reserved(_) => { + #[cfg(not(feature = "no_module"))] + let none = None; + #[cfg(feature = "no_module")] + let none = (); + let s = match input.next().expect(NEVER_ENDS) { (Token::Reserved(s), _) => s, token => unreachable!("Token::Reserved expected but gets {:?}", token), @@ -1407,14 +1425,14 @@ fn parse_primary( Token::LeftParen | Token::Bang if is_keyword_function(&s) => Expr::Variable( None, settings.pos, - (None, None, state.get_identifier("", s)).into(), + (None, none, state.get_identifier("", s)).into(), ), // Access to `this` as a variable is OK within a function scope #[cfg(not(feature = "no_function"))] _ if &*s == KEYWORD_THIS && settings.is_function_scope => Expr::Variable( None, settings.pos, - (None, None, state.get_identifier("", s)).into(), + (None, none, state.get_identifier("", s)).into(), ), // Cannot access to `this` as a variable not in a function scope _ if &*s == KEYWORD_THIS => { @@ -1461,6 +1479,7 @@ fn parse_postfix( lhs = match (lhs, tail_token) { // Qualified function call with ! + #[cfg(not(feature = "no_module"))] (Expr::Variable(_, _, x), Token::Bang) if x.1.is_some() => { return if !match_token(input, Token::LeftParen).0 { Err(LexError::UnexpectedInput(Token::Bang.syntax().to_string()) @@ -1486,17 +1505,37 @@ fn parse_postfix( _ => (), } - let (_, namespace, name) = *x; + let (_, _ns, name) = *x; settings.pos = pos; - let ns = namespace.map(|(ns, _)| ns); - parse_fn_call(input, state, lib, name, true, ns, settings.level_up())? + #[cfg(not(feature = "no_module"))] + let _ns = _ns.map(|(ns, _)| ns); + parse_fn_call( + input, + state, + lib, + name, + true, + #[cfg(not(feature = "no_module"))] + _ns, + settings.level_up(), + )? } // Function call (Expr::Variable(_, pos, x), Token::LeftParen) => { - let (_, namespace, name) = *x; - let ns = namespace.map(|(ns, _)| ns); + let (_, _ns, name) = *x; + #[cfg(not(feature = "no_module"))] + let _ns = _ns.map(|(ns, _)| ns); settings.pos = pos; - parse_fn_call(input, state, lib, name, false, ns, settings.level_up())? + parse_fn_call( + input, + state, + lib, + name, + false, + #[cfg(not(feature = "no_module"))] + _ns, + settings.level_up(), + )? } // module access #[cfg(not(feature = "no_module"))] @@ -1508,7 +1547,7 @@ fn parse_postfix( if let Some((ref mut namespace, _)) = namespace { namespace.push(var_name_def); } else { - let mut ns = Namespace::new(); + let mut ns = crate::module::Namespace::new(); ns.push(var_name_def); namespace = Some((ns, 42)); } @@ -1553,6 +1592,7 @@ fn parse_postfix( } // Cache the hash key for namespace-qualified variables + #[cfg(not(feature = "no_module"))] let namespaced_variable = match lhs { Expr::Variable(_, _, ref mut x) if x.1.is_some() => Some(x.as_mut()), Expr::Index(ref mut x, _, _) | Expr::Dot(ref mut x, _, _) => match x.lhs { @@ -1562,8 +1602,9 @@ fn parse_postfix( _ => None, }; + #[cfg(not(feature = "no_module"))] if let Some((_, Some((namespace, hash)), name)) = namespaced_variable { - *hash = calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); + *hash = crate::calc_qualified_var_hash(namespace.iter().map(|v| v.name.as_str()), name); #[cfg(not(feature = "no_module"))] { @@ -1841,9 +1882,12 @@ fn make_dot_expr( Ok(Expr::Dot(BinaryExpr { lhs, rhs }.into(), false, op_pos)) } // lhs.module::id - syntax error + #[cfg(not(feature = "no_module"))] (_, Expr::Variable(_, _, x)) => { Err(PERR::PropertyExpected.into_err(x.1.expect("`Some`").0[0].pos)) } + #[cfg(feature = "no_module")] + (_, Expr::Variable(_, _, _)) => unreachable!("qualified property name"), // lhs.prop (lhs, prop @ Expr::Property(_, _)) => Ok(Expr::Dot( BinaryExpr { lhs, rhs: prop }.into(), @@ -2157,7 +2201,19 @@ fn parse_custom_syntax( let name = state.get_identifier("", name); segments.push(name.clone().into()); tokens.push(state.get_identifier("", CUSTOM_SYNTAX_MARKER_IDENT)); - inputs.push(Expr::Variable(None, pos, (None, None, name).into())); + inputs.push(Expr::Variable( + None, + pos, + ( + None, + #[cfg(not(feature = "no_module"))] + None, + #[cfg(feature = "no_module")] + (), + name, + ) + .into(), + )); } CUSTOM_SYNTAX_MARKER_SYMBOL => { let (symbol, pos) = parse_symbol(input)?; @@ -3161,12 +3217,21 @@ fn make_curry_from_externals( args.push(fn_expr); - args.extend( - externals - .iter() - .cloned() - .map(|x| Expr::Variable(None, Position::NONE, (None, None, x).into())), - ); + args.extend(externals.iter().cloned().map(|x| { + Expr::Variable( + None, + Position::NONE, + ( + None, + #[cfg(not(feature = "no_module"))] + None, + #[cfg(feature = "no_module")] + (), + x, + ) + .into(), + ) + })); let expr = FnCallExpr { name: state.get_identifier("", crate::engine::KEYWORD_FN_PTR_CURRY), diff --git a/src/serde/metadata.rs b/src/serde/metadata.rs index 38c6f721..42b46a91 100644 --- a/src/serde/metadata.rs +++ b/src/serde/metadata.rs @@ -16,6 +16,7 @@ enum FnType { Native, } +#[cfg(not(feature = "no_module"))] #[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Serialize, Deserialize)] #[serde(rename_all = "camelCase")] enum FnNamespace { @@ -23,6 +24,7 @@ enum FnNamespace { Internal, } +#[cfg(not(feature = "no_module"))] impl From for FnNamespace { fn from(value: crate::FnNamespace) -> Self { match value { @@ -62,6 +64,7 @@ struct FnParam<'a> { struct FnMetadata<'a> { pub base_hash: u64, pub full_hash: u64, + #[cfg(not(feature = "no_module"))] pub namespace: FnNamespace, pub access: FnAccess, pub name: String, @@ -110,6 +113,7 @@ impl<'a> From<&'a FuncInfo> for FnMetadata<'a> { Self { base_hash, full_hash, + #[cfg(not(feature = "no_module"))] namespace: info.metadata.namespace.into(), access: info.metadata.access.into(), name: info.metadata.name.to_string(), @@ -206,6 +210,7 @@ impl Engine { let _ast = ast; let mut global = ModuleMetadata::new(); + #[cfg(not(feature = "no_module"))] for (name, m) in &self.global_sub_modules { global.modules.insert(name, m.as_ref().into()); } @@ -215,15 +220,23 @@ impl Engine { .filter(|m| include_packages || !m.standard) .flat_map(|m| m.iter_fn()) .for_each(|f| { + #[allow(unused_mut)] let mut meta: FnMetadata = f.into(); - meta.namespace = FnNamespace::Global; + #[cfg(not(feature = "no_module"))] + { + meta.namespace = FnNamespace::Global; + } global.functions.push(meta); }); #[cfg(not(feature = "no_function"))] for f in _ast.shared_lib().iter_fn() { + #[allow(unused_mut)] let mut meta: FnMetadata = f.into(); - meta.namespace = FnNamespace::Global; + #[cfg(not(feature = "no_module"))] + { + meta.namespace = FnNamespace::Global; + } global.functions.push(meta); }