Add is_symbol_disabled and is_custom_keyword.
This commit is contained in:
parent
dd0d1dd7ca
commit
0206f776db
@ -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
|
||||||
==============
|
==============
|
||||||
|
@ -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)
|
||||||
|
@ -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)?;
|
||||||
}
|
}
|
||||||
|
@ -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)]
|
||||||
|
@ -928,20 +928,18 @@ 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(|| {
|
||||||
let val = scope.get_mut_by_index(index);
|
Box::new(ERR::ErrorVariableNotFound(var.name.to_string(), var.pos))
|
||||||
|
})?;
|
||||||
|
|
||||||
if !val.is_shared() {
|
let val = scope.get_mut_by_index(index);
|
||||||
// Replace the variable with a shared value.
|
|
||||||
*val = std::mem::take(val).into_shared();
|
if !val.is_shared() {
|
||||||
}
|
// Replace the variable with a shared value.
|
||||||
} else {
|
*val = std::mem::take(val).into_shared();
|
||||||
return Err(
|
|
||||||
ERR::ErrorVariableNotFound(var.name.to_string(), var.pos).into()
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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>();
|
||||||
|
@ -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 {
|
||||||
|
@ -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
|
||||||
|
Loading…
Reference in New Issue
Block a user