diff --git a/CHANGELOG.md b/CHANGELOG.md index 7627368d..7ee71de3 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,11 @@ New features * It is now possible to require a specific _type_ to the `this` pointer for a particular script-defined function so that it is called only when the `this` pointer contains the specified type. * `is_def_fn` is extended to support checking for typed methods, with syntax `is_def_fn(this_type, fn_name, arity)` +Enhancements +------------ + +* `Engine::is_symbol_disabled` is added to test whether a particular keyword/symbol is disabled. + Version 1.13.0 ============== diff --git a/src/api/custom_syntax.rs b/src/api/custom_syntax.rs index e5dfb56c..762b5dcc 100644 --- a/src/api/custom_syntax.rs +++ b/src/api/custom_syntax.rs @@ -259,15 +259,9 @@ impl Engine { // 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 - .as_ref() - .map_or(false, |m| m.contains(s)) + if (self.is_symbol_disabled(s) || token.as_ref().map_or(false, Token::is_reserved)) - && !self - .custom_keywords - .as_ref() - .map_or(false, |m| m.contains_key(s)) + && !self.is_custom_keyword(s) { self.custom_keywords .get_or_insert_with(Default::default) @@ -279,10 +273,7 @@ impl Engine { // 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 - .as_ref() - .map_or(false, |m| m.contains(s)) => + && !self.is_symbol_disabled(s) => { return Err(LexError::ImproperSymbol( s.to_string(), @@ -299,15 +290,9 @@ impl Engine { && (is_valid_identifier(s) || is_reserved_keyword_or_symbol(s).0) => { // Make it a custom keyword/symbol if it is disabled or reserved - if self - .disabled_symbols - .as_ref() - .map_or(false, |m| m.contains(s)) + if self.is_symbol_disabled(s) || (token.as_ref().map_or(false, Token::is_reserved) - && !self - .custom_keywords - .as_ref() - .map_or(false, |m| m.contains_key(s))) + && !self.is_custom_keyword(s)) { self.custom_keywords .get_or_insert_with(Default::default) diff --git a/src/api/definitions/mod.rs b/src/api/definitions/mod.rs index 69224b27..f4fae1d6 100644 --- a/src/api/definitions/mod.rs +++ b/src/api/definitions/mod.rs @@ -457,12 +457,7 @@ impl Module { !f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name); #[cfg(not(feature = "no_custom_syntax"))] - let operator = operator - || def - .engine - .custom_keywords - .as_ref() - .map_or(false, |m| m.contains_key(f.metadata.name.as_str())); + let operator = operator || def.engine.is_custom_keyword(f.metadata.name.as_str()); f.write_definition(writer, def, operator)?; } diff --git a/src/api/mod.rs b/src/api/mod.rs index 21238f5a..44df4213 100644 --- a/src/api/mod.rs +++ b/src/api/mod.rs @@ -109,7 +109,7 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[inline(always)] + #[inline] pub fn disable_symbol(&mut self, symbol: impl Into) -> &mut Self { self.disabled_symbols .get_or_insert_with(Default::default) @@ -117,6 +117,27 @@ impl Engine { self } + /// Is a particular keyword or operator disabled? + /// + /// # Examples + /// + /// ```rust + /// # fn main() -> Result<(), rhai::ParseError> { + /// use rhai::Engine; + /// + /// let mut engine = Engine::new(); + /// + /// engine.disable_symbol("if"); // disable the 'if' keyword + /// + /// assert!(engine.is_symbol_disabled("if")); + /// ``` + #[inline] + pub fn is_symbol_disabled(&self, symbol: &str) -> bool { + self.disabled_symbols + .as_ref() + .map_or(false, |m| m.contains(symbol)) + } + /// Register a custom operator with a precedence into the language. /// /// Not available under `no_custom_syntax`. @@ -168,32 +189,21 @@ impl Engine { Some(Token::Custom(..)) => (), // Active standard keywords cannot be made custom // Disabled keywords are OK - Some(token) if token.is_standard_keyword() => { - 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 - .as_ref() - .map_or(false, |m| m.contains(token.literal_syntax())) - { - return Err(format!("'{keyword}' is a reserved operator")); - } + Some(token) + if token.is_standard_keyword() + && !self.is_symbol_disabled(token.literal_syntax()) => + { + return Err(format!("'{keyword}' is a reserved keyword")) } // Active standard symbols cannot be made custom Some(token) - if !self - .disabled_symbols - .as_ref() - .map_or(false, |m| m.contains(token.literal_syntax())) => + if token.is_standard_symbol() + && !self.is_symbol_disabled(token.literal_syntax()) => { + return Err(format!("'{keyword}' is a reserved operator")) + } + // Active standard symbols cannot be made custom + Some(token) if !self.is_symbol_disabled(token.literal_syntax()) => { return Err(format!("'{keyword}' is a reserved symbol")) } // Disabled symbols are OK @@ -207,6 +217,16 @@ impl Engine { Ok(self) } + /// Is a keyword registered as a custom keyword? + /// + /// Not available under `no_custom_syntax`. + #[cfg(not(feature = "no_custom_syntax"))] + #[inline] + pub(crate) fn is_custom_keyword(&self, keyword: &str) -> bool { + self.custom_keywords + .as_ref() + .map_or(false, |m| m.contains_key(keyword)) + } /// Get the default value of the custom state for each evaluation run. #[inline(always)] diff --git a/src/eval/stmt.rs b/src/eval/stmt.rs index 29ded4a1..9c88f1cf 100644 --- a/src/eval/stmt.rs +++ b/src/eval/stmt.rs @@ -928,20 +928,18 @@ impl Engine { #[cfg(not(feature = "no_closure"))] Stmt::Share(x) => { for (var, index) in &**x { - if let Some(index) = index + let index = index .map(|n| scope.len() - n.get()) .or_else(|| scope.search(&var.name)) - { - let val = scope.get_mut_by_index(index); + .ok_or_else(|| { + Box::new(ERR::ErrorVariableNotFound(var.name.to_string(), var.pos)) + })?; - if !val.is_shared() { - // Replace the variable with a shared value. - *val = std::mem::take(val).into_shared(); - } - } else { - return Err( - ERR::ErrorVariableNotFound(var.name.to_string(), var.pos).into() - ); + let val = scope.get_mut_by_index(index); + + if !val.is_shared() { + // Replace the variable with a shared value. + *val = std::mem::take(val).into_shared(); } } diff --git a/src/func/call.rs b/src/func/call.rs index 6c8413d0..2cd36208 100644 --- a/src/func/call.rs +++ b/src/func/call.rs @@ -604,8 +604,11 @@ impl Engine { }; if error { - let sig = self.gen_fn_call_signature(fn_name, args); - return Err(ERR::ErrorFunctionNotFound(sig, pos).into()); + return Err(ERR::ErrorFunctionNotFound( + self.gen_fn_call_signature(fn_name, args), + pos, + ) + .into()); } } @@ -815,12 +818,16 @@ impl Engine { // Handle obj.call(fn_ptr, ...) KEYWORD_FN_PTR_CALL => { if call_args.is_empty() { - let typ = self.map_type_name(target.type_name()); - return Err(self.make_type_mismatch_err::(typ, fn_call_pos)); + return Err(self.make_type_mismatch_err::( + self.map_type_name(target.type_name()), + fn_call_pos, + )); } if !call_args[0].is_fnptr() { - let typ = self.map_type_name(call_args[0].type_name()); - return Err(self.make_type_mismatch_err::(typ, first_arg_pos)); + return Err(self.make_type_mismatch_err::( + self.map_type_name(call_args[0].type_name()), + first_arg_pos, + )); } // FnPtr call on object @@ -904,8 +911,10 @@ impl Engine { } KEYWORD_FN_PTR_CURRY => { if !target.is_fnptr() { - let typ = self.map_type_name(target.type_name()); - return Err(self.make_type_mismatch_err::(typ, fn_call_pos)); + return Err(self.make_type_mismatch_err::( + self.map_type_name(target.type_name()), + fn_call_pos, + )); } let mut fn_ptr = target.read_lock::().expect("`FnPtr`").clone(); @@ -1036,8 +1045,10 @@ impl Engine { self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?; if !arg_value.is_fnptr() { - let typ = self.map_type_name(arg_value.type_name()); - return Err(self.make_type_mismatch_err::(typ, arg_pos)); + return Err(self.make_type_mismatch_err::( + self.map_type_name(arg_value.type_name()), + arg_pos, + )); } let fn_ptr = arg_value.cast::(); @@ -1122,8 +1133,10 @@ impl Engine { self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?; if !arg_value.is_fnptr() { - let typ = self.map_type_name(arg_value.type_name()); - return Err(self.make_type_mismatch_err::(typ, arg_pos)); + return Err(self.make_type_mismatch_err::( + self.map_type_name(arg_value.type_name()), + arg_pos, + )); } let mut fn_ptr = arg_value.cast::(); diff --git a/src/parser.rs b/src/parser.rs index 00fd7240..a5ba7f1d 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -2431,13 +2431,7 @@ impl Engine { } #[cfg(not(feature = "no_custom_syntax"))] - Token::Custom(s) - if self - .custom_keywords - .as_ref() - .and_then(|m| m.get(s.as_str())) - .map_or(false, Option::is_some) => - { + Token::Custom(s) if self.is_custom_keyword(s.as_str()) => { op_base.hashes = if native_only { FnCallHashes::from_native_only(calc_fn_hash(None, &s, 2)) } else { diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 2e0e009c..0d855cbf 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -2492,7 +2492,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.as_ref().map_or(false, |m| m.contains_key(&*s)), + self.engine.is_custom_keyword(&*s), #[cfg(feature = "no_custom_syntax")] false ) @@ -2529,7 +2529,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.as_ref().map_or(false,|m| m.contains(token)) => { + (token, false) if self.engine.is_symbol_disabled(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()) }, @@ -2538,13 +2538,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.as_ref().map_or(false,|m| m.contains_key(&*s)) => { + Some((Token::Identifier(s), pos)) if self.engine.is_custom_keyword(&*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.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())) { + Some((token, pos)) if token.is_literal() && self.engine.is_custom_keyword(token.literal_syntax()) => { + if self.engine.is_symbol_disabled(token.literal_syntax()) { // Disabled standard keyword/symbol (Token::Custom(Box::new(token.literal_syntax().into())), pos) } else { @@ -2553,7 +2553,7 @@ impl<'a> Iterator for TokenIterator<'a> { } } // Disabled symbol - Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => { + Some((token, pos)) if token.is_literal() && self.engine.is_symbol_disabled(token.literal_syntax()) => { (Token::Reserved(Box::new(token.literal_syntax().into())), pos) } // Normal symbol