Add is_symbol_disabled and is_custom_keyword.

This commit is contained in:
Stephen Chung 2023-04-11 11:38:48 +08:00
parent dd0d1dd7ca
commit 0206f776db
8 changed files with 95 additions and 85 deletions

View File

@ -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. * 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)` * `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 Version 1.13.0
============== ==============

View File

@ -259,15 +259,9 @@ impl Engine {
// Keyword/symbol not in first position // Keyword/symbol not in first position
_ if !segments.is_empty() && token.is_some() => { _ if !segments.is_empty() && token.is_some() => {
// Make it a custom keyword/symbol if it is disabled or reserved // Make it a custom keyword/symbol if it is disabled or reserved
if (self if (self.is_symbol_disabled(s)
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s))
|| token.as_ref().map_or(false, Token::is_reserved)) || token.as_ref().map_or(false, Token::is_reserved))
&& !self && !self.is_custom_keyword(s)
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(s))
{ {
self.custom_keywords self.custom_keywords
.get_or_insert_with(Default::default) .get_or_insert_with(Default::default)
@ -279,10 +273,7 @@ impl Engine {
// Standard keyword in first position but not disabled // Standard keyword in first position but not disabled
_ if segments.is_empty() _ if segments.is_empty()
&& token.as_ref().map_or(false, Token::is_standard_keyword) && token.as_ref().map_or(false, Token::is_standard_keyword)
&& !self && !self.is_symbol_disabled(s) =>
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s)) =>
{ {
return Err(LexError::ImproperSymbol( return Err(LexError::ImproperSymbol(
s.to_string(), s.to_string(),
@ -299,15 +290,9 @@ impl Engine {
&& (is_valid_identifier(s) || is_reserved_keyword_or_symbol(s).0) => && (is_valid_identifier(s) || is_reserved_keyword_or_symbol(s).0) =>
{ {
// Make it a custom keyword/symbol if it is disabled or reserved // Make it a custom keyword/symbol if it is disabled or reserved
if self if self.is_symbol_disabled(s)
.disabled_symbols
.as_ref()
.map_or(false, |m| m.contains(s))
|| (token.as_ref().map_or(false, Token::is_reserved) || (token.as_ref().map_or(false, Token::is_reserved)
&& !self && !self.is_custom_keyword(s))
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(s)))
{ {
self.custom_keywords self.custom_keywords
.get_or_insert_with(Default::default) .get_or_insert_with(Default::default)

View File

@ -457,12 +457,7 @@ impl Module {
!f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name); !f.metadata.name.contains('$') && !is_valid_function_name(&f.metadata.name);
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
let operator = operator let operator = operator || def.engine.is_custom_keyword(f.metadata.name.as_str());
|| def
.engine
.custom_keywords
.as_ref()
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
f.write_definition(writer, def, operator)?; f.write_definition(writer, def, operator)?;
} }

View File

@ -109,7 +109,7 @@ impl Engine {
/// # Ok(()) /// # Ok(())
/// # } /// # }
/// ``` /// ```
#[inline(always)] #[inline]
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self { pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
self.disabled_symbols self.disabled_symbols
.get_or_insert_with(Default::default) .get_or_insert_with(Default::default)
@ -117,6 +117,27 @@ impl Engine {
self 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. /// Register a custom operator with a precedence into the language.
/// ///
/// Not available under `no_custom_syntax`. /// Not available under `no_custom_syntax`.
@ -168,32 +189,21 @@ impl Engine {
Some(Token::Custom(..)) => (), 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_standard_keyword() => { Some(token)
if !self if token.is_standard_keyword()
.disabled_symbols && !self.is_symbol_disabled(token.literal_syntax()) =>
.as_ref()
.map_or(false, |m| m.contains(token.literal_syntax()))
{ {
return Err(format!("'{keyword}' is a reserved keyword")); 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"));
}
} }
// Active standard symbols cannot be made custom // Active standard symbols cannot be made custom
Some(token) Some(token)
if !self if token.is_standard_symbol()
.disabled_symbols && !self.is_symbol_disabled(token.literal_syntax()) =>
.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.is_symbol_disabled(token.literal_syntax()) => {
return Err(format!("'{keyword}' is a reserved symbol")) return Err(format!("'{keyword}' is a reserved symbol"))
} }
// Disabled symbols are OK // Disabled symbols are OK
@ -207,6 +217,16 @@ impl Engine {
Ok(self) 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. /// Get the default value of the custom state for each evaluation run.
#[inline(always)] #[inline(always)]

View File

@ -928,21 +928,19 @@ impl Engine {
#[cfg(not(feature = "no_closure"))] #[cfg(not(feature = "no_closure"))]
Stmt::Share(x) => { Stmt::Share(x) => {
for (var, index) in &**x { for (var, index) in &**x {
if let Some(index) = index let index = index
.map(|n| scope.len() - n.get()) .map(|n| scope.len() - n.get())
.or_else(|| scope.search(&var.name)) .or_else(|| scope.search(&var.name))
{ .ok_or_else(|| {
Box::new(ERR::ErrorVariableNotFound(var.name.to_string(), var.pos))
})?;
let val = scope.get_mut_by_index(index); let val = scope.get_mut_by_index(index);
if !val.is_shared() { if !val.is_shared() {
// Replace the variable with a shared value. // Replace the variable with a shared value.
*val = std::mem::take(val).into_shared(); *val = std::mem::take(val).into_shared();
} }
} else {
return Err(
ERR::ErrorVariableNotFound(var.name.to_string(), var.pos).into()
);
}
} }
Ok(Dynamic::UNIT) Ok(Dynamic::UNIT)

View File

@ -604,8 +604,11 @@ impl Engine {
}; };
if error { if error {
let sig = self.gen_fn_call_signature(fn_name, args); return Err(ERR::ErrorFunctionNotFound(
return Err(ERR::ErrorFunctionNotFound(sig, pos).into()); self.gen_fn_call_signature(fn_name, args),
pos,
)
.into());
} }
} }
@ -815,12 +818,16 @@ impl Engine {
// Handle obj.call(fn_ptr, ...) // Handle obj.call(fn_ptr, ...)
KEYWORD_FN_PTR_CALL => { KEYWORD_FN_PTR_CALL => {
if call_args.is_empty() { if call_args.is_empty() {
let typ = self.map_type_name(target.type_name()); return Err(self.make_type_mismatch_err::<FnPtr>(
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos)); self.map_type_name(target.type_name()),
fn_call_pos,
));
} }
if !call_args[0].is_fnptr() { if !call_args[0].is_fnptr() {
let typ = self.map_type_name(call_args[0].type_name()); return Err(self.make_type_mismatch_err::<FnPtr>(
return Err(self.make_type_mismatch_err::<FnPtr>(typ, first_arg_pos)); self.map_type_name(call_args[0].type_name()),
first_arg_pos,
));
} }
// FnPtr call on object // FnPtr call on object
@ -904,8 +911,10 @@ impl Engine {
} }
KEYWORD_FN_PTR_CURRY => { KEYWORD_FN_PTR_CURRY => {
if !target.is_fnptr() { if !target.is_fnptr() {
let typ = self.map_type_name(target.type_name()); return Err(self.make_type_mismatch_err::<FnPtr>(
return Err(self.make_type_mismatch_err::<FnPtr>(typ, fn_call_pos)); self.map_type_name(target.type_name()),
fn_call_pos,
));
} }
let mut fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`").clone(); let mut fn_ptr = target.read_lock::<FnPtr>().expect("`FnPtr`").clone();
@ -1036,8 +1045,10 @@ impl Engine {
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?; self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), arg)?;
if !arg_value.is_fnptr() { if !arg_value.is_fnptr() {
let typ = self.map_type_name(arg_value.type_name()); return Err(self.make_type_mismatch_err::<FnPtr>(
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos)); self.map_type_name(arg_value.type_name()),
arg_pos,
));
} }
let fn_ptr = arg_value.cast::<FnPtr>(); let fn_ptr = arg_value.cast::<FnPtr>();
@ -1122,8 +1133,10 @@ impl Engine {
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?; self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
if !arg_value.is_fnptr() { if !arg_value.is_fnptr() {
let typ = self.map_type_name(arg_value.type_name()); return Err(self.make_type_mismatch_err::<FnPtr>(
return Err(self.make_type_mismatch_err::<FnPtr>(typ, arg_pos)); self.map_type_name(arg_value.type_name()),
arg_pos,
));
} }
let mut fn_ptr = arg_value.cast::<FnPtr>(); let mut fn_ptr = arg_value.cast::<FnPtr>();

View File

@ -2431,13 +2431,7 @@ impl Engine {
} }
#[cfg(not(feature = "no_custom_syntax"))] #[cfg(not(feature = "no_custom_syntax"))]
Token::Custom(s) Token::Custom(s) if self.is_custom_keyword(s.as_str()) => {
if self
.custom_keywords
.as_ref()
.and_then(|m| m.get(s.as_str()))
.map_or(false, Option::is_some) =>
{
op_base.hashes = if native_only { op_base.hashes = if native_only {
FnCallHashes::from_native_only(calc_fn_hash(None, &s, 2)) FnCallHashes::from_native_only(calc_fn_hash(None, &s, 2))
} else { } else {

View File

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