Merge pull request #711 from schungx/master
Fix optimizer bug for closures.
This commit is contained in:
commit
b2dbd322db
@ -6,10 +6,17 @@ Version 1.14.0
|
|||||||
|
|
||||||
The code hacks that attempt to optimize branch prediction performance are removed because benchmarks do not show any material speed improvements.
|
The code hacks that attempt to optimize branch prediction performance are removed because benchmarks do not show any material speed improvements.
|
||||||
|
|
||||||
|
Buf fixes
|
||||||
|
----------
|
||||||
|
|
||||||
|
* `is_shared` is a reserved keyword and is now handled properly (e.g. it cannot be the target of a function pointer).
|
||||||
|
* Re-optimizing an AST via `optimize_ast` with constants now works correctly for closures. Previously the hidden `Share` nodes are not removed and causes variable-not-found errors during runtime if the constants are not available in the scope.
|
||||||
|
|
||||||
New features
|
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)`
|
||||||
|
|
||||||
|
|
||||||
Version 1.13.0
|
Version 1.13.0
|
||||||
|
35
scripts/function_decl5.rhai
Normal file
35
scripts/function_decl5.rhai
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
//! This script defines multiple versions of the same function
|
||||||
|
//! for use as method with different data types.
|
||||||
|
|
||||||
|
// For strings
|
||||||
|
fn string.calc(x) {
|
||||||
|
this.len + x
|
||||||
|
}
|
||||||
|
// For integers
|
||||||
|
fn int.calc(x) {
|
||||||
|
this * x
|
||||||
|
}
|
||||||
|
// For booleans
|
||||||
|
fn bool.calc(x) {
|
||||||
|
if this { x } else { 0}
|
||||||
|
}
|
||||||
|
// For arrays
|
||||||
|
fn array.calc(x) {
|
||||||
|
this.len + x
|
||||||
|
}
|
||||||
|
// For object maps
|
||||||
|
fn map.calc(x) {
|
||||||
|
this[x]
|
||||||
|
}
|
||||||
|
// Catch-all
|
||||||
|
fn calc(x) {
|
||||||
|
`${this}: ${x}`
|
||||||
|
}
|
||||||
|
|
||||||
|
print("hello".calc(42)); // 47
|
||||||
|
print(42.calc(42)); // 1764
|
||||||
|
print(true.calc(42)); // 42
|
||||||
|
print(false.calc(42)); // 0
|
||||||
|
print([1,2,3].calc(42)); // 45
|
||||||
|
print(#{"a": 1, "b": 2}.calc("b")); // 2
|
||||||
|
print('x'.calc(42)); // x: 42
|
@ -233,7 +233,7 @@ impl Engine {
|
|||||||
let rewind_scope = options.rewind_scope;
|
let rewind_scope = options.rewind_scope;
|
||||||
|
|
||||||
let result = if options.eval_ast && !statements.is_empty() {
|
let result = if options.eval_ast && !statements.is_empty() {
|
||||||
auto_restore! {
|
defer! {
|
||||||
scope if rewind_scope => rewind;
|
scope if rewind_scope => rewind;
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
}
|
}
|
||||||
|
@ -261,12 +261,12 @@ impl Engine {
|
|||||||
// 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
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.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
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(s))
|
.map_or(false, |m| m.contains_key(s))
|
||||||
{
|
{
|
||||||
self.custom_keywords
|
self.custom_keywords
|
||||||
@ -281,7 +281,7 @@ impl Engine {
|
|||||||
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
&& token.as_ref().map_or(false, Token::is_standard_keyword)
|
||||||
&& !self
|
&& !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains(s)) =>
|
.map_or(false, |m| m.contains(s)) =>
|
||||||
{
|
{
|
||||||
return Err(LexError::ImproperSymbol(
|
return Err(LexError::ImproperSymbol(
|
||||||
@ -301,12 +301,12 @@ impl Engine {
|
|||||||
// 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
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.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
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(s)))
|
.map_or(false, |m| m.contains_key(s)))
|
||||||
{
|
{
|
||||||
self.custom_keywords
|
self.custom_keywords
|
||||||
|
@ -112,6 +112,21 @@ fn curry(fn_ptr: FnPtr, ...args: ?) -> FnPtr;
|
|||||||
/// ```
|
/// ```
|
||||||
fn is_def_fn(fn_name: String, num_params: int) -> bool;
|
fn is_def_fn(fn_name: String, num_params: int) -> bool;
|
||||||
|
|
||||||
|
/// Return `true` if a script-defined function exists with a specified name and
|
||||||
|
/// number of parameters, bound to a specified type for `this`.
|
||||||
|
///
|
||||||
|
/// # Example
|
||||||
|
///
|
||||||
|
/// ```rhai
|
||||||
|
/// // A method that requires `this` to be `MyType`
|
||||||
|
/// fn MyType.foo(x) { }
|
||||||
|
///
|
||||||
|
/// print(is_def_fn("MyType", "foo", 1)); // prints true
|
||||||
|
/// print(is_def_fn("foo", 1)); // prints false
|
||||||
|
/// print(is_def_fn("MyType", "foo", 2)); // prints false
|
||||||
|
/// ```
|
||||||
|
fn is_def_fn(this_type: String, fn_name: String, num_params: int) -> bool;
|
||||||
|
|
||||||
/// Return `true` if a variable matching a specified name is defined.
|
/// Return `true` if a variable matching a specified name is defined.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
|
@ -372,7 +372,7 @@ impl Definitions<'_> {
|
|||||||
let mut m = self
|
let mut m = self
|
||||||
.engine
|
.engine
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(move |(name, module)| {
|
.map(move |(name, module)| {
|
||||||
@ -461,7 +461,7 @@ impl Module {
|
|||||||
|| def
|
|| def
|
||||||
.engine
|
.engine
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
.map_or(false, |m| m.contains_key(f.metadata.name.as_str()));
|
||||||
|
|
||||||
f.write_definition(writer, def, operator)?;
|
f.write_definition(writer, def, operator)?;
|
||||||
|
@ -242,7 +242,7 @@ impl Engine {
|
|||||||
ast.resolver().cloned(),
|
ast.resolver().cloned(),
|
||||||
);
|
);
|
||||||
|
|
||||||
auto_restore! { global => move |g| {
|
defer! { global => move |g| {
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
{
|
{
|
||||||
g.embedded_module_resolver = orig_embedded_module_resolver;
|
g.embedded_module_resolver = orig_embedded_module_resolver;
|
||||||
|
@ -208,7 +208,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
return self
|
return self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.find_map(|(_, m)| m.get_custom_type(name));
|
.find_map(|(_, m)| m.get_custom_type(name));
|
||||||
@ -243,7 +243,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
return self
|
return self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.find_map(|(_, m)| m.get_custom_type(name));
|
.find_map(|(_, m)| m.get_custom_type(name));
|
||||||
|
@ -171,7 +171,7 @@ impl Engine {
|
|||||||
Some(token) if token.is_standard_keyword() => {
|
Some(token) if token.is_standard_keyword() => {
|
||||||
if !self
|
if !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
.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"));
|
||||||
@ -181,7 +181,7 @@ impl Engine {
|
|||||||
Some(token) if token.is_standard_symbol() => {
|
Some(token) if token.is_standard_symbol() => {
|
||||||
if !self
|
if !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax()))
|
.map_or(false, |m| m.contains(token.literal_syntax()))
|
||||||
{
|
{
|
||||||
return Err(format!("'{keyword}' is a reserved operator"));
|
return Err(format!("'{keyword}' is a reserved operator"));
|
||||||
@ -191,7 +191,7 @@ impl Engine {
|
|||||||
Some(token)
|
Some(token)
|
||||||
if !self
|
if !self
|
||||||
.disabled_symbols
|
.disabled_symbols
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
.map_or(false, |m| m.contains(token.literal_syntax())) =>
|
||||||
{
|
{
|
||||||
return Err(format!("'{keyword}' is a reserved symbol"))
|
return Err(format!("'{keyword}' is a reserved symbol"))
|
||||||
|
@ -786,7 +786,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
for (name, m) in self.global_sub_modules.as_deref().into_iter().flatten() {
|
for (name, m) in self.global_sub_modules.as_ref().into_iter().flatten() {
|
||||||
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
|
signatures.extend(m.gen_fn_signatures().map(|f| format!("{name}::{f}")));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -839,6 +839,8 @@ impl AST {
|
|||||||
///
|
///
|
||||||
/// { // <- new block scope
|
/// { // <- new block scope
|
||||||
/// const Z = 0; // <- literal constant not at top-level
|
/// const Z = 0; // <- literal constant not at top-level
|
||||||
|
///
|
||||||
|
/// print(Z); // make sure the block is not optimized away
|
||||||
/// }
|
/// }
|
||||||
/// ")?;
|
/// ")?;
|
||||||
///
|
///
|
||||||
|
@ -276,9 +276,9 @@ pub enum Expr {
|
|||||||
/// [String][ImmutableString] constant.
|
/// [String][ImmutableString] constant.
|
||||||
StringConstant(ImmutableString, Position),
|
StringConstant(ImmutableString, Position),
|
||||||
/// An interpolated [string][ImmutableString].
|
/// An interpolated [string][ImmutableString].
|
||||||
InterpolatedString(Box<StaticVec<Expr>>, Position),
|
InterpolatedString(Box<FnArgsVec<Expr>>, Position),
|
||||||
/// [ expr, ... ]
|
/// [ expr, ... ]
|
||||||
Array(Box<StaticVec<Expr>>, Position),
|
Array(Box<FnArgsVec<Expr>>, Position),
|
||||||
/// #{ name:expr, ... }
|
/// #{ name:expr, ... }
|
||||||
Map(
|
Map(
|
||||||
Box<(StaticVec<(Ident, Expr)>, BTreeMap<Identifier, Dynamic>)>,
|
Box<(StaticVec<(Ident, Expr)>, BTreeMap<Identifier, Dynamic>)>,
|
||||||
@ -288,8 +288,8 @@ pub enum Expr {
|
|||||||
Unit(Position),
|
Unit(Position),
|
||||||
/// Variable access - (optional long index, namespace, namespace hash, variable name), optional short index, position
|
/// Variable access - (optional long index, namespace, namespace hash, variable name), optional short index, position
|
||||||
///
|
///
|
||||||
/// The short index is [`u8`] which is used when the index is <= 255, which should be the vast
|
/// The short index is [`u8`] which is used when the index is <= 255, which should be
|
||||||
/// majority of cases (unless there are more than 255 variables defined!).
|
/// the vast majority of cases (unless there are more than 255 variables defined!).
|
||||||
/// This is to avoid reading a pointer redirection during each variable access.
|
/// This is to avoid reading a pointer redirection during each variable access.
|
||||||
Variable(
|
Variable(
|
||||||
Box<(Option<NonZeroUsize>, Namespace, u64, ImmutableString)>,
|
Box<(Option<NonZeroUsize>, Namespace, u64, ImmutableString)>,
|
||||||
@ -315,15 +315,15 @@ pub enum Expr {
|
|||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
///
|
///
|
||||||
/// [`NEGATED`][ASTFlags::NEGATED] = `?.` (`.` if unset)
|
/// * [`NEGATED`][ASTFlags::NEGATED] = `?.` (`.` if unset)
|
||||||
/// [`BREAK`][ASTFlags::BREAK] = terminate the chain (recurse into the chain if unset)
|
/// * [`BREAK`][ASTFlags::BREAK] = terminate the chain (recurse into the chain if unset)
|
||||||
Dot(Box<BinaryExpr>, ASTFlags, Position),
|
Dot(Box<BinaryExpr>, ASTFlags, Position),
|
||||||
/// lhs `[` rhs `]`
|
/// lhs `[` rhs `]`
|
||||||
///
|
///
|
||||||
/// ### Flags
|
/// ### Flags
|
||||||
///
|
///
|
||||||
/// [`NEGATED`][ASTFlags::NEGATED] = `?[` ... `]` (`[` ... `]` if unset)
|
/// * [`NEGATED`][ASTFlags::NEGATED] = `?[` ... `]` (`[` ... `]` if unset)
|
||||||
/// [`BREAK`][ASTFlags::BREAK] = terminate the chain (recurse into the chain if unset)
|
/// * [`BREAK`][ASTFlags::BREAK] = terminate the chain (recurse into the chain if unset)
|
||||||
Index(Box<BinaryExpr>, ASTFlags, Position),
|
Index(Box<BinaryExpr>, ASTFlags, Position),
|
||||||
/// lhs `&&` rhs
|
/// lhs `&&` rhs
|
||||||
And(Box<BinaryExpr>, Position),
|
And(Box<BinaryExpr>, Position),
|
||||||
|
@ -5,56 +5,56 @@ use std::ops::{Deref, DerefMut};
|
|||||||
use std::prelude::v1::*;
|
use std::prelude::v1::*;
|
||||||
|
|
||||||
/// Automatically restore state at the end of the scope.
|
/// Automatically restore state at the end of the scope.
|
||||||
macro_rules! auto_restore {
|
macro_rules! defer {
|
||||||
(let $temp:ident = $var:ident . $prop:ident; $code:stmt) => {
|
(let $temp:ident = $var:ident . $prop:ident; $code:stmt) => {
|
||||||
auto_restore!(let $temp = $var.$prop; $code => move |v| v.$prop = $temp);
|
defer!(let $temp = $var.$prop; $code => move |v| v.$prop = $temp);
|
||||||
};
|
};
|
||||||
(let $temp:ident = $var:ident . $prop:ident; $code:stmt => $restore:expr) => {
|
(let $temp:ident = $var:ident . $prop:ident; $code:stmt => $restore:expr) => {
|
||||||
let $temp = $var.$prop;
|
let $temp = $var.$prop;
|
||||||
$code
|
$code
|
||||||
auto_restore!($var => $restore);
|
defer!($var => $restore);
|
||||||
};
|
};
|
||||||
($var:ident => $restore:ident; let $temp:ident = $save:expr;) => {
|
($var:ident => $restore:ident; let $temp:ident = $save:expr;) => {
|
||||||
auto_restore!($var => $restore; let $temp = $save; {});
|
defer!($var => $restore; let $temp = $save; {});
|
||||||
};
|
};
|
||||||
($var:ident if $guard:expr => $restore:ident; let $temp:ident = $save:expr;) => {
|
($var:ident if $guard:expr => $restore:ident; let $temp:ident = $save:expr;) => {
|
||||||
auto_restore!($var if $guard => $restore; let $temp = $save; {});
|
defer!($var if $guard => $restore; let $temp = $save; {});
|
||||||
};
|
};
|
||||||
($var:ident => $restore:ident; let $temp:ident = $save:expr; $code:stmt) => {
|
($var:ident => $restore:ident; let $temp:ident = $save:expr; $code:stmt) => {
|
||||||
let $temp = $save;
|
let $temp = $save;
|
||||||
$code
|
$code
|
||||||
auto_restore!($var => move |v| { v.$restore($temp); });
|
defer!($var => move |v| { v.$restore($temp); });
|
||||||
};
|
};
|
||||||
($var:ident if $guard:expr => $restore:ident; let $temp:ident = $save:expr; $code:stmt) => {
|
($var:ident if $guard:expr => $restore:ident; let $temp:ident = $save:expr; $code:stmt) => {
|
||||||
let $temp = $save;
|
let $temp = $save;
|
||||||
$code
|
$code
|
||||||
auto_restore!($var if $guard => move |v| { v.$restore($temp); });
|
defer!($var if $guard => move |v| { v.$restore($temp); });
|
||||||
};
|
};
|
||||||
($var:ident => $restore:expr) => {
|
($var:ident => $restore:expr) => {
|
||||||
auto_restore!($var = $var => $restore);
|
defer!($var = $var => $restore);
|
||||||
};
|
};
|
||||||
($var:ident = $value:expr => $restore:expr) => {
|
($var:ident = $value:expr => $restore:expr) => {
|
||||||
let $var = &mut *crate::RestoreOnDrop::lock($value, $restore);
|
let $var = &mut *crate::Deferred::lock($value, $restore);
|
||||||
};
|
};
|
||||||
($var:ident if Some($guard:ident) => $restore:expr) => {
|
($var:ident if Some($guard:ident) => $restore:expr) => {
|
||||||
auto_restore!($var = ($var) if Some($guard) => $restore);
|
defer!($var = ($var) if Some($guard) => $restore);
|
||||||
};
|
};
|
||||||
($var:ident = ( $value:expr ) if Some($guard:ident) => $restore:expr) => {
|
($var:ident = ( $value:expr ) if Some($guard:ident) => $restore:expr) => {
|
||||||
let mut __rx__;
|
let mut __rx__;
|
||||||
let $var = if let Some($guard) = $guard {
|
let $var = if let Some($guard) = $guard {
|
||||||
__rx__ = crate::RestoreOnDrop::lock($value, $restore);
|
__rx__ = crate::Deferred::lock($value, $restore);
|
||||||
&mut *__rx__
|
&mut *__rx__
|
||||||
} else {
|
} else {
|
||||||
&mut *$value
|
&mut *$value
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
($var:ident if $guard:expr => $restore:expr) => {
|
($var:ident if $guard:expr => $restore:expr) => {
|
||||||
auto_restore!($var = ($var) if $guard => $restore);
|
defer!($var = ($var) if $guard => $restore);
|
||||||
};
|
};
|
||||||
($var:ident = ( $value:expr ) if $guard:expr => $restore:expr) => {
|
($var:ident = ( $value:expr ) if $guard:expr => $restore:expr) => {
|
||||||
let mut __rx__;
|
let mut __rx__;
|
||||||
let $var = if $guard {
|
let $var = if $guard {
|
||||||
__rx__ = crate::RestoreOnDrop::lock($value, $restore);
|
__rx__ = crate::Deferred::lock($value, $restore);
|
||||||
&mut *__rx__
|
&mut *__rx__
|
||||||
} else {
|
} else {
|
||||||
&mut *$value
|
&mut *$value
|
||||||
@ -64,13 +64,13 @@ macro_rules! auto_restore {
|
|||||||
|
|
||||||
/// Run custom restoration logic upon the end of scope.
|
/// Run custom restoration logic upon the end of scope.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub struct RestoreOnDrop<'a, T: ?Sized, R: FnOnce(&mut T)> {
|
pub struct Deferred<'a, T: ?Sized, R: FnOnce(&mut T)> {
|
||||||
value: &'a mut T,
|
lock: &'a mut T,
|
||||||
restore: Option<R>,
|
defer: Option<R>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
|
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Deferred<'a, T, R> {
|
||||||
/// Create a new [`RestoreOnDrop`] that locks a mutable reference and runs restoration logic at
|
/// Create a new [`Deferred`] that locks a mutable reference and runs restoration logic at
|
||||||
/// the end of scope.
|
/// the end of scope.
|
||||||
///
|
///
|
||||||
/// Beware that the end of scope means the end of its lifetime, not necessarily waiting until
|
/// Beware that the end of scope means the end of its lifetime, not necessarily waiting until
|
||||||
@ -78,31 +78,31 @@ impl<'a, T: ?Sized, R: FnOnce(&mut T)> RestoreOnDrop<'a, T, R> {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn lock(value: &'a mut T, restore: R) -> Self {
|
pub fn lock(value: &'a mut T, restore: R) -> Self {
|
||||||
Self {
|
Self {
|
||||||
value,
|
lock: value,
|
||||||
restore: Some(restore),
|
defer: Some(restore),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Drop for RestoreOnDrop<'a, T, R> {
|
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Drop for Deferred<'a, T, R> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn drop(&mut self) {
|
fn drop(&mut self) {
|
||||||
self.restore.take().unwrap()(self.value);
|
self.defer.take().unwrap()(self.lock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Deref for RestoreOnDrop<'a, T, R> {
|
impl<'a, T: ?Sized, R: FnOnce(&mut T)> Deref for Deferred<'a, T, R> {
|
||||||
type Target = T;
|
type Target = T;
|
||||||
|
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref(&self) -> &Self::Target {
|
fn deref(&self) -> &Self::Target {
|
||||||
self.value
|
self.lock
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, T: ?Sized, R: FnOnce(&mut T)> DerefMut for RestoreOnDrop<'a, T, R> {
|
impl<'a, T: ?Sized, R: FnOnce(&mut T)> DerefMut for Deferred<'a, T, R> {
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
fn deref_mut(&mut self) -> &mut Self::Target {
|
fn deref_mut(&mut self) -> &mut Self::Target {
|
||||||
self.value
|
self.lock
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -96,8 +96,7 @@ pub struct Engine {
|
|||||||
pub(crate) global_modules: StaticVec<SharedModule>,
|
pub(crate) global_modules: StaticVec<SharedModule>,
|
||||||
/// A collection of all sub-modules directly loaded into the Engine.
|
/// A collection of all sub-modules directly loaded into the Engine.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
pub(crate) global_sub_modules:
|
pub(crate) global_sub_modules: Option<std::collections::BTreeMap<Identifier, SharedModule>>,
|
||||||
Option<Box<std::collections::BTreeMap<Identifier, SharedModule>>>,
|
|
||||||
|
|
||||||
/// A module resolution service.
|
/// A module resolution service.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -107,15 +106,14 @@ pub struct Engine {
|
|||||||
pub(crate) interned_strings: Option<Box<Locked<StringsInterner>>>,
|
pub(crate) interned_strings: Option<Box<Locked<StringsInterner>>>,
|
||||||
|
|
||||||
/// A set of symbols to disable.
|
/// A set of symbols to disable.
|
||||||
pub(crate) disabled_symbols: Option<Box<BTreeSet<Identifier>>>,
|
pub(crate) disabled_symbols: Option<BTreeSet<Identifier>>,
|
||||||
/// A map containing custom keywords and precedence to recognize.
|
/// A map containing custom keywords and precedence to recognize.
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
pub(crate) custom_keywords:
|
pub(crate) custom_keywords: Option<std::collections::BTreeMap<Identifier, Option<Precedence>>>,
|
||||||
Option<Box<std::collections::BTreeMap<Identifier, Option<Precedence>>>>,
|
|
||||||
/// Custom syntax.
|
/// Custom syntax.
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
pub(crate) custom_syntax: Option<
|
pub(crate) custom_syntax: Option<
|
||||||
Box<std::collections::BTreeMap<Identifier, Box<crate::api::custom_syntax::CustomSyntax>>>,
|
std::collections::BTreeMap<Identifier, Box<crate::api::custom_syntax::CustomSyntax>>,
|
||||||
>,
|
>,
|
||||||
/// Callback closure for filtering variable definition.
|
/// Callback closure for filtering variable definition.
|
||||||
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
|
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
|
||||||
|
@ -67,7 +67,7 @@ impl Engine {
|
|||||||
idx: &mut Dynamic,
|
idx: &mut Dynamic,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<Dynamic> {
|
) -> RhaiResultOf<Dynamic> {
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
defer! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let hash = hash_idx().0;
|
let hash = hash_idx().0;
|
||||||
let args = &mut [target, idx];
|
let args = &mut [target, idx];
|
||||||
@ -88,7 +88,7 @@ impl Engine {
|
|||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
defer! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let hash = hash_idx().1;
|
let hash = hash_idx().1;
|
||||||
let args = &mut [target, idx, new_val];
|
let args = &mut [target, idx, new_val];
|
||||||
@ -344,8 +344,6 @@ impl Engine {
|
|||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
new_val: Option<(Dynamic, &OpAssignment)>,
|
new_val: Option<(Dynamic, &OpAssignment)>,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
let chain_type = ChainType::from(expr);
|
|
||||||
|
|
||||||
let BinaryExpr { lhs, rhs } = match expr {
|
let BinaryExpr { lhs, rhs } = match expr {
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
Expr::Index(x, ..) => &**x,
|
Expr::Index(x, ..) => &**x,
|
||||||
@ -356,27 +354,22 @@ impl Engine {
|
|||||||
|
|
||||||
let idx_values = &mut FnArgsVec::new_const();
|
let idx_values = &mut FnArgsVec::new_const();
|
||||||
|
|
||||||
match rhs {
|
match (rhs, ChainType::from(expr)) {
|
||||||
// Short-circuit for simple property access: {expr}.prop
|
// Short-circuit for simple property access: {expr}.prop
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(..) if chain_type == ChainType::Dotting => (),
|
(Expr::Property(..), ChainType::Dotting) => (),
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
(Expr::Property(..), ..) => {
|
||||||
// Short-circuit for simple method call: {expr}.func()
|
unreachable!("unexpected Expr::Property for indexing")
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::FnCall(x, ..) if chain_type == ChainType::Dotting && x.args.is_empty() => (),
|
|
||||||
// Short-circuit for method call with all literal arguments: {expr}.func(1, 2, 3)
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::FnCall(x, ..)
|
|
||||||
if chain_type == ChainType::Dotting && x.args.iter().all(Expr::is_constant) =>
|
|
||||||
{
|
|
||||||
idx_values.extend(x.args.iter().map(|expr| expr.get_literal_value().unwrap()))
|
|
||||||
}
|
}
|
||||||
// Short-circuit for indexing with literal: {expr}[1]
|
// Short-circuit for indexing with literal: {expr}[1]
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if chain_type == ChainType::Indexing && rhs.is_constant() => {
|
(_, ChainType::Indexing) if rhs.is_constant() => {
|
||||||
idx_values.push(rhs.get_literal_value().unwrap())
|
idx_values.push(rhs.get_literal_value().unwrap())
|
||||||
}
|
}
|
||||||
|
// Short-circuit for simple method call: {expr}.func()
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
(Expr::MethodCall(x, ..), ChainType::Dotting) if x.args.is_empty() => (),
|
||||||
// All other patterns - evaluate the arguments chain
|
// All other patterns - evaluate the arguments chain
|
||||||
_ => self.eval_dot_index_chain_arguments(
|
_ => self.eval_dot_index_chain_arguments(
|
||||||
global,
|
global,
|
||||||
@ -394,12 +387,13 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "debugging"))]
|
#[cfg(not(feature = "debugging"))]
|
||||||
let scope2 = ();
|
let scope2 = ();
|
||||||
|
|
||||||
match lhs {
|
match (lhs, new_val) {
|
||||||
// id.??? or id[???]
|
// id.??? or id[???]
|
||||||
Expr::Variable(.., var_pos) => {
|
(Expr::Variable(.., var_pos), new_val) => {
|
||||||
|
self.track_operation(global, *var_pos)?;
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), lhs)?;
|
self.run_debugger(global, caches, scope, this_ptr.as_deref_mut(), lhs)?;
|
||||||
self.track_operation(global, *var_pos)?;
|
|
||||||
|
|
||||||
let target = &mut self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
let target = &mut self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||||
|
|
||||||
@ -408,9 +402,9 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// {expr}.??? = ??? or {expr}[???] = ???
|
// {expr}.??? = ??? or {expr}[???] = ???
|
||||||
_ if new_val.is_some() => unreachable!("cannot assign to an expression"),
|
(_, Some(..)) => unreachable!("cannot assign to an expression"),
|
||||||
// {expr}.??? or {expr}[???]
|
// {expr}.??? or {expr}[???]
|
||||||
lhs_expr => {
|
(lhs_expr, None) => {
|
||||||
let value = self
|
let value = self
|
||||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs_expr)?
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs_expr)?
|
||||||
.flatten();
|
.flatten();
|
||||||
@ -418,7 +412,7 @@ impl Engine {
|
|||||||
|
|
||||||
self.eval_dot_index_chain_raw(
|
self.eval_dot_index_chain_raw(
|
||||||
global, caches, scope2, this_ptr, lhs_expr, expr, obj_ptr, rhs, idx_values,
|
global, caches, scope2, this_ptr, lhs_expr, expr, obj_ptr, rhs, idx_values,
|
||||||
new_val,
|
None,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -438,27 +432,25 @@ impl Engine {
|
|||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
self.track_operation(global, expr.position())?;
|
self.track_operation(global, expr.position())?;
|
||||||
|
|
||||||
let chain_type = ChainType::from(parent);
|
match (expr, ChainType::from(parent)) {
|
||||||
|
|
||||||
match expr {
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::MethodCall(x, ..) if chain_type == ChainType::Dotting && !x.is_qualified() => {
|
(Expr::MethodCall(x, ..), ChainType::Dotting) => {
|
||||||
|
debug_assert!(
|
||||||
|
!x.is_qualified(),
|
||||||
|
"method call in dot chain should not be namespace-qualified"
|
||||||
|
);
|
||||||
|
|
||||||
for expr in &x.args {
|
for expr in &x.args {
|
||||||
let arg_value =
|
let arg_value =
|
||||||
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||||
idx_values.push(arg_value.0.flatten());
|
idx_values.push(arg_value.0.flatten());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::MethodCall(..) if chain_type == ChainType::Dotting => {
|
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(..) if chain_type == ChainType::Dotting => (),
|
(Expr::Property(..), ChainType::Dotting) => (),
|
||||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
|
||||||
|
|
||||||
Expr::Index(x, ..) | Expr::Dot(x, ..)
|
(Expr::Index(x, ..) | Expr::Dot(x, ..), chain_type)
|
||||||
if !parent.options().contains(ASTFlags::BREAK) =>
|
if !parent.options().contains(ASTFlags::BREAK) =>
|
||||||
{
|
{
|
||||||
let BinaryExpr { lhs, rhs, .. } = &**x;
|
let BinaryExpr { lhs, rhs, .. } = &**x;
|
||||||
@ -466,37 +458,34 @@ impl Engine {
|
|||||||
let mut _arg_values = FnArgsVec::new_const();
|
let mut _arg_values = FnArgsVec::new_const();
|
||||||
|
|
||||||
// Evaluate in left-to-right order
|
// Evaluate in left-to-right order
|
||||||
match lhs {
|
match (lhs, chain_type) {
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::Property(..) if chain_type == ChainType::Dotting => (),
|
(Expr::Property(..), ChainType::Dotting) => (),
|
||||||
Expr::Property(..) => unreachable!("unexpected Expr::Property for indexing"),
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Expr::MethodCall(x, ..)
|
(Expr::MethodCall(x, ..), ChainType::Dotting) => {
|
||||||
if chain_type == ChainType::Dotting && !x.is_qualified() =>
|
debug_assert!(
|
||||||
{
|
!x.is_qualified(),
|
||||||
|
"method call in dot chain should not be namespace-qualified"
|
||||||
|
);
|
||||||
|
|
||||||
for expr in &x.args {
|
for expr in &x.args {
|
||||||
let tp = this_ptr.as_deref_mut();
|
let tp = this_ptr.as_deref_mut();
|
||||||
let arg_value = self.get_arg_value(global, caches, scope, tp, expr)?;
|
let arg_value = self.get_arg_value(global, caches, scope, tp, expr)?;
|
||||||
_arg_values.push(arg_value.0.flatten());
|
_arg_values.push(arg_value.0.flatten());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
Expr::MethodCall(..) if chain_type == ChainType::Dotting => {
|
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
expr if chain_type == ChainType::Dotting => {
|
|
||||||
unreachable!("invalid dot expression: {:?}", expr);
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if chain_type == ChainType::Indexing => {
|
(_, ChainType::Indexing) => {
|
||||||
_arg_values.push(
|
_arg_values.push(
|
||||||
self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs)?
|
self.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), lhs)?
|
||||||
.flatten(),
|
.flatten(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
expr => unreachable!("unknown chained expression: {:?}", expr),
|
#[allow(unreachable_patterns)]
|
||||||
|
(expr, chain_type) => {
|
||||||
|
unreachable!("unknown {:?} expression: {:?}", chain_type, expr)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Push in reverse order
|
// Push in reverse order
|
||||||
@ -507,16 +496,13 @@ impl Engine {
|
|||||||
idx_values.extend(_arg_values);
|
idx_values.extend(_arg_values);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "no_object"))]
|
|
||||||
_ if chain_type == ChainType::Dotting => {
|
|
||||||
unreachable!("invalid dot expression: {:?}", expr);
|
|
||||||
}
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ if chain_type == ChainType::Indexing => idx_values.push(
|
(_, ChainType::Indexing) => idx_values.push(
|
||||||
self.eval_expr(global, caches, scope, this_ptr, expr)?
|
self.eval_expr(global, caches, scope, this_ptr, expr)?
|
||||||
.flatten(),
|
.flatten(),
|
||||||
),
|
),
|
||||||
_ => unreachable!("unknown chained expression: {:?}", expr),
|
#[allow(unreachable_patterns)]
|
||||||
|
(expr, chain_type) => unreachable!("unknown {:?} expression: {:?}", chain_type, expr),
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
@ -554,9 +540,9 @@ impl Engine {
|
|||||||
|
|
||||||
let pos = rhs.start_position();
|
let pos = rhs.start_position();
|
||||||
|
|
||||||
match rhs {
|
match (rhs, new_val) {
|
||||||
// xxx[idx].expr... | xxx[idx][expr]...
|
// xxx[idx].expr... | xxx[idx][expr]...
|
||||||
Expr::Dot(x, ..) | Expr::Index(x, ..)
|
(Expr::Dot(x, ..) | Expr::Index(x, ..), new_val)
|
||||||
if !parent.options().contains(ASTFlags::BREAK) =>
|
if !parent.options().contains(ASTFlags::BREAK) =>
|
||||||
{
|
{
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
@ -603,11 +589,10 @@ impl Engine {
|
|||||||
Ok(result)
|
Ok(result)
|
||||||
}
|
}
|
||||||
// xxx[rhs] op= new_val
|
// xxx[rhs] op= new_val
|
||||||
_ if new_val.is_some() => {
|
(_, Some((new_val, op_info))) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr, parent)?;
|
self.run_debugger(global, caches, scope, this_ptr, parent)?;
|
||||||
|
|
||||||
let (new_val, op_info) = new_val.expect("`Some`");
|
|
||||||
let idx_val = &mut idx_values.pop().unwrap();
|
let idx_val = &mut idx_values.pop().unwrap();
|
||||||
let idx = &mut idx_val.clone();
|
let idx = &mut idx_val.clone();
|
||||||
|
|
||||||
@ -660,7 +645,7 @@ impl Engine {
|
|||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// xxx[rhs]
|
// xxx[rhs]
|
||||||
_ => {
|
(_, None) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr, parent)?;
|
self.run_debugger(global, caches, scope, this_ptr, parent)?;
|
||||||
|
|
||||||
@ -681,21 +666,30 @@ impl Engine {
|
|||||||
return Ok((Dynamic::UNIT, false));
|
return Ok((Dynamic::UNIT, false));
|
||||||
}
|
}
|
||||||
|
|
||||||
match rhs {
|
match (rhs, new_val, target.is_map()) {
|
||||||
|
// xxx.fn_name(...) = ???
|
||||||
|
(Expr::MethodCall(..), Some(..), ..) => {
|
||||||
|
unreachable!("method call cannot be assigned to")
|
||||||
|
}
|
||||||
// xxx.fn_name(arg_expr_list)
|
// xxx.fn_name(arg_expr_list)
|
||||||
Expr::MethodCall(x, pos) if !x.is_qualified() && new_val.is_none() => {
|
(Expr::MethodCall(x, pos), None, ..) => {
|
||||||
|
debug_assert!(
|
||||||
|
!x.is_qualified(),
|
||||||
|
"method call in dot chain should not be namespace-qualified"
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset =
|
let reset =
|
||||||
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger_with_reset(global, caches, scope, this_ptr, rhs)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
auto_restore! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
defer! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -704,21 +698,12 @@ impl Engine {
|
|||||||
global, caches, name, *hashes, target, call_args, arg1_pos, *pos,
|
global, caches, name, *hashes, target, call_args, arg1_pos, *pos,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.fn_name(...) = ???
|
|
||||||
Expr::MethodCall(..) if new_val.is_some() => {
|
|
||||||
unreachable!("method call cannot be assigned to")
|
|
||||||
}
|
|
||||||
// xxx.module::fn_name(...) - syntax error
|
|
||||||
Expr::MethodCall(..) => {
|
|
||||||
unreachable!("function call in dot chain should not be namespace-qualified")
|
|
||||||
}
|
|
||||||
// {xxx:map}.id op= ???
|
// {xxx:map}.id op= ???
|
||||||
Expr::Property(x, pos) if new_val.is_some() && target.is_map() => {
|
(Expr::Property(x, pos), Some((new_val, op_info)), true) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
||||||
|
|
||||||
let index = &mut x.2.clone().into();
|
let index = &mut x.2.clone().into();
|
||||||
let (new_val, op_info) = new_val.expect("`Some`");
|
|
||||||
{
|
{
|
||||||
let val_target = &mut self.get_indexed_mut(
|
let val_target = &mut self.get_indexed_mut(
|
||||||
global, caches, target, index, *pos, op_pos, true, false,
|
global, caches, target, index, *pos, op_pos, true, false,
|
||||||
@ -731,7 +716,7 @@ impl Engine {
|
|||||||
Ok((Dynamic::UNIT, true))
|
Ok((Dynamic::UNIT, true))
|
||||||
}
|
}
|
||||||
// {xxx:map}.id
|
// {xxx:map}.id
|
||||||
Expr::Property(x, pos) if target.is_map() => {
|
(Expr::Property(x, pos), None, true) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
||||||
|
|
||||||
@ -742,12 +727,11 @@ impl Engine {
|
|||||||
Ok((val.take_or_clone(), false))
|
Ok((val.take_or_clone(), false))
|
||||||
}
|
}
|
||||||
// xxx.id op= ???
|
// xxx.id op= ???
|
||||||
Expr::Property(x, pos) if new_val.is_some() => {
|
(Expr::Property(x, pos), Some((mut new_val, op_info)), false) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
||||||
|
|
||||||
let ((getter, hash_get), (setter, hash_set), name) = &**x;
|
let ((getter, hash_get), (setter, hash_set), name) = &**x;
|
||||||
let (mut new_val, op_info) = new_val.expect("`Some`");
|
|
||||||
|
|
||||||
if op_info.is_op_assignment() {
|
if op_info.is_op_assignment() {
|
||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
@ -807,7 +791,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
// xxx.id
|
// xxx.id
|
||||||
Expr::Property(x, pos) => {
|
(Expr::Property(x, pos), None, false) => {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
self.run_debugger(global, caches, scope, this_ptr, rhs)?;
|
||||||
|
|
||||||
@ -836,7 +820,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
// {xxx:map}.sub_lhs[expr] | {xxx:map}.sub_lhs.expr
|
||||||
Expr::Index(x, ..) | Expr::Dot(x, ..) if target.is_map() => {
|
(Expr::Index(x, ..) | Expr::Dot(x, ..), new_val, true) => {
|
||||||
let _node = &x.lhs;
|
let _node = &x.lhs;
|
||||||
let mut _this_ptr = this_ptr;
|
let mut _this_ptr = this_ptr;
|
||||||
let _tp = _this_ptr.as_deref_mut();
|
let _tp = _this_ptr.as_deref_mut();
|
||||||
@ -852,19 +836,24 @@ impl Engine {
|
|||||||
)?
|
)?
|
||||||
}
|
}
|
||||||
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
// {xxx:map}.fn_name(arg_expr_list)[expr] | {xxx:map}.fn_name(arg_expr_list).expr
|
||||||
Expr::MethodCall(ref x, pos) if !x.is_qualified() => {
|
Expr::MethodCall(ref x, pos) => {
|
||||||
|
debug_assert!(
|
||||||
|
!x.is_qualified(),
|
||||||
|
"method call in dot chain should not be namespace-qualified"
|
||||||
|
);
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self
|
let reset = self
|
||||||
.run_debugger_with_reset(global, caches, scope, _tp, _node)?;
|
.run_debugger_with_reset(global, caches, scope, _tp, _node)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**x;
|
} = &**x;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
auto_restore! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
defer! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
let arg1_pos = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -875,10 +864,6 @@ impl Engine {
|
|||||||
.0
|
.0
|
||||||
.into()
|
.into()
|
||||||
}
|
}
|
||||||
// {xxx:map}.module::fn_name(...) - syntax error
|
|
||||||
Expr::MethodCall(..) => unreachable!(
|
|
||||||
"function call in dot chain should not be namespace-qualified"
|
|
||||||
),
|
|
||||||
// Others - syntax error
|
// Others - syntax error
|
||||||
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||||
};
|
};
|
||||||
@ -889,7 +874,7 @@ impl Engine {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
|
// xxx.sub_lhs[expr] | xxx.sub_lhs.expr
|
||||||
Expr::Index(x, ..) | Expr::Dot(x, ..) => {
|
(Expr::Index(x, ..) | Expr::Dot(x, ..), new_val, ..) => {
|
||||||
let _node = &x.lhs;
|
let _node = &x.lhs;
|
||||||
let mut _this_ptr = this_ptr;
|
let mut _this_ptr = this_ptr;
|
||||||
let _tp = _this_ptr.as_deref_mut();
|
let _tp = _this_ptr.as_deref_mut();
|
||||||
@ -970,21 +955,26 @@ impl Engine {
|
|||||||
Ok((result, may_be_changed))
|
Ok((result, may_be_changed))
|
||||||
}
|
}
|
||||||
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
// xxx.fn_name(arg_expr_list)[expr] | xxx.fn_name(arg_expr_list).expr
|
||||||
Expr::MethodCall(ref f, pos) if !f.is_qualified() => {
|
Expr::MethodCall(ref f, pos) => {
|
||||||
|
debug_assert!(
|
||||||
|
!f.is_qualified(),
|
||||||
|
"method call in dot chain should not be namespace-qualified"
|
||||||
|
);
|
||||||
|
|
||||||
let val = {
|
let val = {
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = self.run_debugger_with_reset(
|
let reset = self.run_debugger_with_reset(
|
||||||
global, caches, scope, _tp, _node,
|
global, caches, scope, _tp, _node,
|
||||||
)?;
|
)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
||||||
|
|
||||||
let crate::ast::FnCallExpr {
|
let crate::ast::FnCallExpr {
|
||||||
name, hashes, args, ..
|
name, hashes, args, ..
|
||||||
} = &**f;
|
} = &**f;
|
||||||
|
|
||||||
// Truncate the index values upon exit
|
// Truncate the index values upon exit
|
||||||
auto_restore! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
defer! { idx_values => truncate; let offset = idx_values.len() - args.len(); }
|
||||||
|
|
||||||
let call_args = &mut idx_values[offset..];
|
let call_args = &mut idx_values[offset..];
|
||||||
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
let pos1 = args.get(0).map_or(Position::NONE, Expr::position);
|
||||||
@ -1002,16 +992,12 @@ impl Engine {
|
|||||||
idx_values, new_val,
|
idx_values, new_val,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
// xxx.module::fn_name(...) - syntax error
|
|
||||||
Expr::MethodCall(..) => unreachable!(
|
|
||||||
"function call in dot chain should not be namespace-qualified"
|
|
||||||
),
|
|
||||||
// Others - syntax error
|
// Others - syntax error
|
||||||
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
ref expr => unreachable!("invalid dot expression: {:?}", expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Syntax error
|
// Syntax error
|
||||||
expr => unreachable!("invalid chaining expression: {:?}", expr),
|
(expr, ..) => unreachable!("invalid chaining expression: {:?}", expr),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -236,16 +236,13 @@ impl Engine {
|
|||||||
mut this_ptr: Option<&mut Dynamic>,
|
mut this_ptr: Option<&mut Dynamic>,
|
||||||
expr: &Expr,
|
expr: &Expr,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
// Coded this way for better branch prediction.
|
self.track_operation(global, expr.position())?;
|
||||||
// Popular branches are lifted out of the `match` statement into their own branches.
|
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset =
|
let reset =
|
||||||
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
||||||
|
|
||||||
self.track_operation(global, expr.position())?;
|
|
||||||
|
|
||||||
match expr {
|
match expr {
|
||||||
// Constants
|
// Constants
|
||||||
@ -262,17 +259,18 @@ impl Engine {
|
|||||||
self.eval_fn_call_expr(global, caches, scope, this_ptr, x, *pos)
|
self.eval_fn_call_expr(global, caches, scope, this_ptr, x, *pos)
|
||||||
}
|
}
|
||||||
|
|
||||||
Expr::Variable(x, index, var_pos) => {
|
Expr::Variable(x, index, var_pos)
|
||||||
if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS {
|
if index.is_none() && x.0.is_none() && x.3 == KEYWORD_THIS =>
|
||||||
this_ptr
|
{
|
||||||
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
|
this_ptr
|
||||||
.cloned()
|
.ok_or_else(|| ERR::ErrorUnboundThis(*var_pos).into())
|
||||||
} else {
|
.cloned()
|
||||||
self.search_namespace(global, caches, scope, this_ptr, expr)
|
|
||||||
.map(Target::take_or_clone)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Expr::Variable(..) => self
|
||||||
|
.search_namespace(global, caches, scope, this_ptr, expr)
|
||||||
|
.map(Target::take_or_clone),
|
||||||
|
|
||||||
Expr::InterpolatedString(x, _) => {
|
Expr::InterpolatedString(x, _) => {
|
||||||
let mut concat = SmartString::new_const();
|
let mut concat = SmartString::new_const();
|
||||||
|
|
||||||
|
106
src/eval/stmt.rs
106
src/eval/stmt.rs
@ -43,7 +43,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Restore scope at end of block if necessary
|
// Restore scope at end of block if necessary
|
||||||
auto_restore! { scope if restore_orig_state => rewind; let orig_scope_len = scope.len(); }
|
defer! { scope if restore_orig_state => rewind; let orig_scope_len = scope.len(); }
|
||||||
|
|
||||||
// Restore global state at end of block if necessary
|
// Restore global state at end of block if necessary
|
||||||
let orig_always_search_scope = global.always_search_scope;
|
let orig_always_search_scope = global.always_search_scope;
|
||||||
@ -54,7 +54,7 @@ impl Engine {
|
|||||||
global.scope_level += 1;
|
global.scope_level += 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto_restore! { global if restore_orig_state => move |g| {
|
defer! { global if restore_orig_state => move |g| {
|
||||||
g.scope_level -= 1;
|
g.scope_level -= 1;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
@ -66,7 +66,7 @@ impl Engine {
|
|||||||
}}
|
}}
|
||||||
|
|
||||||
// Pop new function resolution caches at end of block
|
// Pop new function resolution caches at end of block
|
||||||
auto_restore! {
|
defer! {
|
||||||
caches => rewind_fn_resolution_caches;
|
caches => rewind_fn_resolution_caches;
|
||||||
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
|
let orig_fn_resolution_caches_len = caches.fn_resolution_caches_len();
|
||||||
}
|
}
|
||||||
@ -209,7 +209,7 @@ impl Engine {
|
|||||||
get_builtin_op_assignment_fn(op_x, &*lock_guard, &new_val)
|
get_builtin_op_assignment_fn(op_x, &*lock_guard, &new_val)
|
||||||
{
|
{
|
||||||
// We may not need to bump the level because built-in's do not need it.
|
// We may not need to bump the level because built-in's do not need it.
|
||||||
//auto_restore! { let orig_level = global.level; global.level += 1 }
|
//defer! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||||
let context = need_context
|
let context = need_context
|
||||||
@ -267,13 +267,13 @@ impl Engine {
|
|||||||
stmt: &Stmt,
|
stmt: &Stmt,
|
||||||
rewind_scope: bool,
|
rewind_scope: bool,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
|
self.track_operation(global, stmt.position())?;
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset =
|
let reset =
|
||||||
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), stmt)?;
|
self.run_debugger_with_reset(global, caches, scope, this_ptr.as_deref_mut(), stmt)?;
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
||||||
|
|
||||||
self.track_operation(global, stmt.position())?;
|
|
||||||
|
|
||||||
match stmt {
|
match stmt {
|
||||||
// No-op
|
// No-op
|
||||||
@ -307,6 +307,8 @@ impl Engine {
|
|||||||
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
|
.eval_expr(global, caches, scope, this_ptr.as_deref_mut(), rhs)?
|
||||||
.flatten();
|
.flatten();
|
||||||
|
|
||||||
|
self.track_operation(global, lhs.position())?;
|
||||||
|
|
||||||
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
let mut target = self.search_namespace(global, caches, scope, this_ptr, lhs)?;
|
||||||
|
|
||||||
let is_temp_result = !target.is_ref();
|
let is_temp_result = !target.is_ref();
|
||||||
@ -326,8 +328,6 @@ impl Engine {
|
|||||||
.into());
|
.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
self.track_operation(global, lhs.position())?;
|
|
||||||
|
|
||||||
self.eval_op_assignment(global, caches, op_info, lhs, &mut target, rhs_val)?;
|
self.eval_op_assignment(global, caches, op_info, lhs, &mut target, rhs_val)?;
|
||||||
} else {
|
} else {
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
@ -664,7 +664,7 @@ impl Engine {
|
|||||||
.or_else(|| global.get_iter(iter_type))
|
.or_else(|| global.get_iter(iter_type))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.global_sub_modules
|
self.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.find_map(|(_, m)| m.get_qualified_iter(iter_type))
|
.find_map(|(_, m)| m.get_qualified_iter(iter_type))
|
||||||
@ -673,7 +673,7 @@ impl Engine {
|
|||||||
let iter_func = iter_func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
|
let iter_func = iter_func.ok_or_else(|| ERR::ErrorFor(expr.start_position()))?;
|
||||||
|
|
||||||
// Restore scope at end of statement
|
// Restore scope at end of statement
|
||||||
auto_restore! { scope => rewind; let orig_scope_len = scope.len(); }
|
defer! { scope => rewind; let orig_scope_len = scope.len(); }
|
||||||
|
|
||||||
// Add the loop variables
|
// Add the loop variables
|
||||||
let counter_index = (!counter.is_empty()).then(|| {
|
let counter_index = (!counter.is_empty()).then(|| {
|
||||||
@ -686,53 +686,53 @@ impl Engine {
|
|||||||
|
|
||||||
let mut result = Dynamic::UNIT;
|
let mut result = Dynamic::UNIT;
|
||||||
|
|
||||||
for (x, iter_value) in iter_func(iter_obj).enumerate() {
|
if body.is_empty() {
|
||||||
// Increment counter
|
for _ in iter_func(iter_obj) {
|
||||||
if let Some(counter_index) = counter_index {
|
self.track_operation(global, body.position())?;
|
||||||
// As the variable increments from 0, this should always work
|
}
|
||||||
// since any overflow will first be caught below.
|
} else {
|
||||||
let index_value = x as INT;
|
for (x, iter_value) in iter_func(iter_obj).enumerate() {
|
||||||
|
// Increment counter
|
||||||
|
if let Some(counter_index) = counter_index {
|
||||||
|
// As the variable increments from 0, this should always work
|
||||||
|
// since any overflow will first be caught below.
|
||||||
|
let index_value = x as INT;
|
||||||
|
|
||||||
#[cfg(not(feature = "unchecked"))]
|
#[cfg(not(feature = "unchecked"))]
|
||||||
#[allow(clippy::absurd_extreme_comparisons)]
|
#[allow(clippy::absurd_extreme_comparisons)]
|
||||||
if index_value > crate::MAX_USIZE_INT {
|
if index_value > crate::MAX_USIZE_INT {
|
||||||
return Err(ERR::ErrorArithmetic(
|
return Err(ERR::ErrorArithmetic(
|
||||||
format!("for-loop counter overflow: {x}"),
|
format!("for-loop counter overflow: {x}"),
|
||||||
counter.pos,
|
counter.pos,
|
||||||
)
|
)
|
||||||
.into());
|
.into());
|
||||||
|
}
|
||||||
|
|
||||||
|
*scope.get_mut_by_index(counter_index).write_lock().unwrap() =
|
||||||
|
Dynamic::from_int(index_value);
|
||||||
}
|
}
|
||||||
|
|
||||||
*scope.get_mut_by_index(counter_index).write_lock().unwrap() =
|
// Set loop value
|
||||||
Dynamic::from_int(index_value);
|
let value = iter_value
|
||||||
}
|
.map_err(|err| err.fill_position(expr.position()))?
|
||||||
|
.flatten();
|
||||||
|
|
||||||
// Set loop value
|
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
||||||
let value = iter_value
|
|
||||||
.map_err(|err| err.fill_position(expr.position()))?
|
|
||||||
.flatten();
|
|
||||||
|
|
||||||
*scope.get_mut_by_index(index).write_lock().unwrap() = value;
|
// Run block
|
||||||
|
let this_ptr = this_ptr.as_deref_mut();
|
||||||
|
|
||||||
// Run block
|
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
||||||
self.track_operation(global, body.position())?;
|
Ok(_) => (),
|
||||||
|
Err(err) => match *err {
|
||||||
if body.is_empty() {
|
ERR::LoopBreak(false, ..) => (),
|
||||||
continue;
|
ERR::LoopBreak(true, value, ..) => {
|
||||||
}
|
result = value;
|
||||||
|
break;
|
||||||
let this_ptr = this_ptr.as_deref_mut();
|
}
|
||||||
|
_ => return Err(err),
|
||||||
match self.eval_stmt_block(global, caches, scope, this_ptr, body, true) {
|
},
|
||||||
Ok(_) => (),
|
}
|
||||||
Err(err) => match *err {
|
|
||||||
ERR::LoopBreak(false, ..) => (),
|
|
||||||
ERR::LoopBreak(true, value, ..) => {
|
|
||||||
result = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => return Err(err),
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -808,7 +808,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// Restore scope at end of block
|
// Restore scope at end of block
|
||||||
auto_restore! { scope if !catch_var.is_unit() => rewind; let orig_scope_len = scope.len(); }
|
defer! { scope if !catch_var.is_unit() => rewind; let orig_scope_len = scope.len(); }
|
||||||
|
|
||||||
if let Expr::Variable(x, ..) = catch_var {
|
if let Expr::Variable(x, ..) = catch_var {
|
||||||
scope.push(x.3.clone(), err_value);
|
scope.push(x.3.clone(), err_value);
|
||||||
|
171
src/func/call.rs
171
src/func/call.rs
@ -206,7 +206,7 @@ impl Engine {
|
|||||||
.or_else(|| _global.get_qualified_fn(hash, true))
|
.or_else(|| _global.get_qualified_fn(hash, true))
|
||||||
.or_else(|| {
|
.or_else(|| {
|
||||||
self.global_sub_modules
|
self.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.filter(|(_, m)| m.contains_indexed_global_functions())
|
.filter(|(_, m)| m.contains_indexed_global_functions())
|
||||||
@ -248,7 +248,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let is_dynamic = is_dynamic
|
let is_dynamic = is_dynamic
|
||||||
|| _global.may_contain_dynamic_fn(hash_base)
|
|| _global.may_contain_dynamic_fn(hash_base)
|
||||||
|| self.global_sub_modules.as_deref().map_or(false, |m| {
|
|| self.global_sub_modules.as_ref().map_or(false, |m| {
|
||||||
m.values().any(|m| m.may_contain_dynamic_fn(hash_base))
|
m.values().any(|m| m.may_contain_dynamic_fn(hash_base))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -488,7 +488,7 @@ impl Engine {
|
|||||||
// index getter function not found?
|
// index getter function not found?
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
crate::engine::FN_IDX_GET => {
|
crate::engine::FN_IDX_GET => {
|
||||||
assert!(args.len() == 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
let t0 = self.map_type_name(args[0].type_name());
|
let t0 = self.map_type_name(args[0].type_name());
|
||||||
let t1 = self.map_type_name(args[1].type_name());
|
let t1 = self.map_type_name(args[1].type_name());
|
||||||
@ -499,7 +499,7 @@ impl Engine {
|
|||||||
// index setter function not found?
|
// index setter function not found?
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
crate::engine::FN_IDX_SET => {
|
crate::engine::FN_IDX_SET => {
|
||||||
assert!(args.len() == 3);
|
debug_assert_eq!(args.len(), 3);
|
||||||
|
|
||||||
let t0 = self.map_type_name(args[0].type_name());
|
let t0 = self.map_type_name(args[0].type_name());
|
||||||
let t1 = self.map_type_name(args[1].type_name());
|
let t1 = self.map_type_name(args[1].type_name());
|
||||||
@ -511,7 +511,7 @@ impl Engine {
|
|||||||
// Getter function not found?
|
// Getter function not found?
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
_ if name.starts_with(crate::engine::FN_GET) => {
|
_ if name.starts_with(crate::engine::FN_GET) => {
|
||||||
assert!(args.len() == 1);
|
debug_assert_eq!(args.len(), 1);
|
||||||
|
|
||||||
let prop = &name[crate::engine::FN_GET.len()..];
|
let prop = &name[crate::engine::FN_GET.len()..];
|
||||||
let t0 = self.map_type_name(args[0].type_name());
|
let t0 = self.map_type_name(args[0].type_name());
|
||||||
@ -528,7 +528,7 @@ impl Engine {
|
|||||||
// Setter function not found?
|
// Setter function not found?
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
_ if name.starts_with(crate::engine::FN_SET) => {
|
_ if name.starts_with(crate::engine::FN_SET) => {
|
||||||
assert!(args.len() == 2);
|
debug_assert_eq!(args.len(), 2);
|
||||||
|
|
||||||
let prop = &name[crate::engine::FN_SET.len()..];
|
let prop = &name[crate::engine::FN_SET.len()..];
|
||||||
let t0 = self.map_type_name(args[0].type_name());
|
let t0 = self.map_type_name(args[0].type_name());
|
||||||
@ -573,57 +573,46 @@ impl Engine {
|
|||||||
_is_method_call: bool,
|
_is_method_call: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResultOf<(Dynamic, bool)> {
|
) -> RhaiResultOf<(Dynamic, bool)> {
|
||||||
|
// These may be redirected from method style calls.
|
||||||
|
if hashes.is_native_only() {
|
||||||
|
let error = match fn_name {
|
||||||
|
// Handle type_of()
|
||||||
|
KEYWORD_TYPE_OF => {
|
||||||
|
if args.len() == 1 {
|
||||||
|
let typ = self.get_interned_string(self.map_type_name(args[0].type_name()));
|
||||||
|
return Ok((typ.into(), false));
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
crate::engine::KEYWORD_IS_SHARED => {
|
||||||
|
if args.len() == 1 {
|
||||||
|
return Ok((args[0].is_shared().into(), false));
|
||||||
|
}
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
crate::engine::KEYWORD_IS_DEF_FN => true,
|
||||||
|
|
||||||
|
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL
|
||||||
|
| KEYWORD_FN_PTR_CURRY => true,
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
|
if error {
|
||||||
|
let sig = self.gen_fn_call_signature(fn_name, args);
|
||||||
|
return Err(ERR::ErrorFunctionNotFound(sig, pos).into());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check for data race.
|
// Check for data race.
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
ensure_no_data_race(fn_name, args, is_ref_mut)?;
|
ensure_no_data_race(fn_name, args, is_ref_mut)?;
|
||||||
|
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
defer! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
// These may be redirected from method style calls.
|
|
||||||
if hashes.is_native_only() {
|
|
||||||
match fn_name {
|
|
||||||
// Handle type_of()
|
|
||||||
KEYWORD_TYPE_OF if args.len() == 1 => {
|
|
||||||
let typ = self.get_interned_string(self.map_type_name(args[0].type_name()));
|
|
||||||
return Ok((typ.into(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle is_def_fn()
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
crate::engine::KEYWORD_IS_DEF_FN
|
|
||||||
if args.len() == 2 && args[0].is_fnptr() && args[1].is_int() =>
|
|
||||||
{
|
|
||||||
let fn_name = args[0].read_lock::<ImmutableString>().expect("`FnPtr`");
|
|
||||||
let num_params = args[1].as_int().expect("`INT`");
|
|
||||||
|
|
||||||
return Ok((
|
|
||||||
if (0..=crate::MAX_USIZE_INT).contains(&num_params) {
|
|
||||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
|
||||||
let hash_script =
|
|
||||||
calc_fn_hash(None, fn_name.as_str(), num_params as usize);
|
|
||||||
self.has_script_fn(global, caches, hash_script)
|
|
||||||
} else {
|
|
||||||
false
|
|
||||||
}
|
|
||||||
.into(),
|
|
||||||
false,
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
// Handle is_shared()
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
|
||||||
crate::engine::KEYWORD_IS_SHARED => {
|
|
||||||
unreachable!("{} called as method", fn_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
KEYWORD_FN_PTR | KEYWORD_EVAL | KEYWORD_IS_DEF_VAR | KEYWORD_FN_PTR_CALL
|
|
||||||
| KEYWORD_FN_PTR_CURRY => {
|
|
||||||
unreachable!("{} called as method", fn_name)
|
|
||||||
}
|
|
||||||
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Script-defined function call?
|
// Script-defined function call?
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -668,7 +657,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let orig_source = mem::replace(&mut global.source, source.clone());
|
let orig_source = mem::replace(&mut global.source, source.clone());
|
||||||
auto_restore! { global => move |g| g.source = orig_source }
|
defer! { global => move |g| g.source = orig_source }
|
||||||
|
|
||||||
return if _is_method_call {
|
return if _is_method_call {
|
||||||
// Method call of script function - map first argument to `this`
|
// Method call of script function - map first argument to `this`
|
||||||
@ -696,7 +685,7 @@ impl Engine {
|
|||||||
backup.change_first_arg_to_copy(args);
|
backup.change_first_arg_to_copy(args);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto_restore! { args = (args) if swap => move |a| backup.restore_first_arg(a) }
|
defer! { args = (args) if swap => move |a| backup.restore_first_arg(a) }
|
||||||
|
|
||||||
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
||||||
}
|
}
|
||||||
@ -740,7 +729,7 @@ impl Engine {
|
|||||||
})
|
})
|
||||||
});
|
});
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
auto_restore! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
defer! { global if Some(reset) => move |g| g.debugger_mut().reset_status(reset) }
|
||||||
|
|
||||||
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
self.eval_expr(global, caches, scope, this_ptr, arg_expr)
|
||||||
.map(|r| (r, arg_expr.start_position()))
|
.map(|r| (r, arg_expr.start_position()))
|
||||||
@ -1110,7 +1099,7 @@ impl Engine {
|
|||||||
FnCallHashes::from_hash(calc_fn_hash(None, name, args_len))
|
FnCallHashes::from_hash(calc_fn_hash(None, name, args_len))
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// Handle Fn()
|
// Handle Fn(fn_name)
|
||||||
KEYWORD_FN_PTR if total_args == 1 => {
|
KEYWORD_FN_PTR if total_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
@ -1125,7 +1114,7 @@ impl Engine {
|
|||||||
.map_err(|err| err.fill_position(arg_pos));
|
.map_err(|err| err.fill_position(arg_pos));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle curry()
|
// Handle curry(x, ...)
|
||||||
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
KEYWORD_FN_PTR_CURRY if total_args > 1 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
@ -1148,7 +1137,7 @@ impl Engine {
|
|||||||
return Ok(fn_ptr.into());
|
return Ok(fn_ptr.into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_shared()
|
// Handle is_shared(var)
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
crate::engine::KEYWORD_IS_SHARED if total_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
@ -1157,7 +1146,7 @@ impl Engine {
|
|||||||
return Ok(arg_value.is_shared().into());
|
return Ok(arg_value.is_shared().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_fn()
|
// Handle is_def_fn(fn_name, arity)
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
crate::engine::KEYWORD_IS_DEF_FN if total_args == 2 => {
|
||||||
let first = first_arg.unwrap();
|
let first = first_arg.unwrap();
|
||||||
@ -1175,17 +1164,54 @@ impl Engine {
|
|||||||
.as_int()
|
.as_int()
|
||||||
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
||||||
|
|
||||||
return Ok(if (0..=crate::MAX_USIZE_INT).contains(&num_params) {
|
return Ok(if num_params >= 0 {
|
||||||
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||||
let hash_script = calc_fn_hash(None, &fn_name, num_params as usize);
|
let hash_script = calc_fn_hash(None, &fn_name, num_params as usize);
|
||||||
self.has_script_fn(global, caches, hash_script)
|
self.has_script_fn(global, caches, hash_script).into()
|
||||||
} else {
|
} else {
|
||||||
false
|
Dynamic::FALSE
|
||||||
}
|
});
|
||||||
.into());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle is_def_var()
|
// Handle is_def_fn(this_type, fn_name, arity)
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
crate::engine::KEYWORD_IS_DEF_FN if total_args == 3 => {
|
||||||
|
let first = first_arg.unwrap();
|
||||||
|
let (arg_value, arg_pos) =
|
||||||
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), first)?;
|
||||||
|
|
||||||
|
let this_type = arg_value
|
||||||
|
.into_immutable_string()
|
||||||
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
|
let (arg_value, arg_pos) =
|
||||||
|
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), &a_expr[0])?;
|
||||||
|
|
||||||
|
let fn_name = arg_value
|
||||||
|
.into_immutable_string()
|
||||||
|
.map_err(|typ| self.make_type_mismatch_err::<ImmutableString>(typ, arg_pos))?;
|
||||||
|
|
||||||
|
let (arg_value, arg_pos) =
|
||||||
|
self.get_arg_value(global, caches, scope, this_ptr, &a_expr[1])?;
|
||||||
|
|
||||||
|
let num_params = arg_value
|
||||||
|
.as_int()
|
||||||
|
.map_err(|typ| self.make_type_mismatch_err::<crate::INT>(typ, arg_pos))?;
|
||||||
|
|
||||||
|
return Ok(if num_params >= 0 {
|
||||||
|
#[allow(clippy::cast_sign_loss, clippy::cast_possible_truncation)]
|
||||||
|
let hash_script = crate::calc_typed_method_hash(
|
||||||
|
calc_fn_hash(None, &fn_name, num_params as usize),
|
||||||
|
&this_type,
|
||||||
|
);
|
||||||
|
self.has_script_fn(global, caches, hash_script).into()
|
||||||
|
} else {
|
||||||
|
Dynamic::FALSE
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Handle is_def_var(fn_name)
|
||||||
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
KEYWORD_IS_DEF_VAR if total_args == 1 => {
|
||||||
let arg = first_arg.unwrap();
|
let arg = first_arg.unwrap();
|
||||||
let (arg_value, arg_pos) =
|
let (arg_value, arg_pos) =
|
||||||
@ -1196,7 +1222,7 @@ impl Engine {
|
|||||||
return Ok(scope.contains(&var_name).into());
|
return Ok(scope.contains(&var_name).into());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Handle eval()
|
// Handle eval(script)
|
||||||
KEYWORD_EVAL if total_args == 1 => {
|
KEYWORD_EVAL if total_args == 1 => {
|
||||||
// eval - only in function call style
|
// eval - only in function call style
|
||||||
let orig_scope_len = scope.len();
|
let orig_scope_len = scope.len();
|
||||||
@ -1365,10 +1391,11 @@ impl Engine {
|
|||||||
|
|
||||||
// Get target reference to first argument
|
// Get target reference to first argument
|
||||||
let first_arg = &args_expr[0];
|
let first_arg = &args_expr[0];
|
||||||
let target = self.search_scope_only(global, caches, scope, this_ptr, first_arg)?;
|
|
||||||
|
|
||||||
self.track_operation(global, first_arg.position())?;
|
self.track_operation(global, first_arg.position())?;
|
||||||
|
|
||||||
|
let target = self.search_scope_only(global, caches, scope, this_ptr, first_arg)?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
let target_is_shared = target.is_shared();
|
let target_is_shared = target.is_shared();
|
||||||
#[cfg(feature = "no_closure")]
|
#[cfg(feature = "no_closure")]
|
||||||
@ -1433,8 +1460,6 @@ impl Engine {
|
|||||||
}),
|
}),
|
||||||
);
|
);
|
||||||
|
|
||||||
self.track_operation(global, pos)?;
|
|
||||||
|
|
||||||
if let Some(f) = module.get_qualified_fn(hash_qualified_fn) {
|
if let Some(f) = module.get_qualified_fn(hash_qualified_fn) {
|
||||||
func = Some(f);
|
func = Some(f);
|
||||||
break;
|
break;
|
||||||
@ -1452,7 +1477,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto_restore! { let orig_level = global.level; global.level += 1 }
|
defer! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
match func {
|
match func {
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -1463,7 +1488,7 @@ impl Engine {
|
|||||||
let scope = &mut Scope::new();
|
let scope = &mut Scope::new();
|
||||||
|
|
||||||
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
let orig_source = mem::replace(&mut global.source, module.id_raw().cloned());
|
||||||
auto_restore! { global => move |g| g.source = orig_source }
|
defer! { global => move |g| g.source = orig_source }
|
||||||
|
|
||||||
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
self.call_script_fn(global, caches, scope, None, environ, f, args, true, pos)
|
||||||
}
|
}
|
||||||
@ -1730,7 +1755,7 @@ impl Engine {
|
|||||||
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
||||||
{
|
{
|
||||||
// We may not need to bump the level because built-in's do not need it.
|
// We may not need to bump the level because built-in's do not need it.
|
||||||
//auto_restore! { let orig_level = global.level; global.level += 1 }
|
//defer! { let orig_level = global.level; global.level += 1 }
|
||||||
|
|
||||||
let context =
|
let context =
|
||||||
need_context.then(|| (self, name.as_str(), None, &*global, pos).into());
|
need_context.then(|| (self, name.as_str(), None, &*global, pos).into());
|
||||||
|
@ -35,7 +35,7 @@ impl Engine {
|
|||||||
rewind_scope: bool,
|
rewind_scope: bool,
|
||||||
pos: Position,
|
pos: Position,
|
||||||
) -> RhaiResult {
|
) -> RhaiResult {
|
||||||
assert_eq!(fn_def.params.len(), args.len());
|
debug_assert_eq!(fn_def.params.len(), args.len());
|
||||||
|
|
||||||
self.track_operation(global, pos)?;
|
self.track_operation(global, pos)?;
|
||||||
|
|
||||||
@ -219,7 +219,7 @@ impl Engine {
|
|||||||
// Then check imported modules
|
// Then check imported modules
|
||||||
global.contains_qualified_fn(hash_script)
|
global.contains_qualified_fn(hash_script)
|
||||||
// Then check sub-modules
|
// Then check sub-modules
|
||||||
|| self.global_sub_modules.as_deref().map_or(false, |m| {
|
|| self.global_sub_modules.as_ref().map_or(false, |m| {
|
||||||
m.values().any(|m| m.contains_qualified_fn(hash_script))
|
m.values().any(|m| m.contains_qualified_fn(hash_script))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,7 +102,7 @@ use std::prelude::v1::*;
|
|||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod reify;
|
mod reify;
|
||||||
#[macro_use]
|
#[macro_use]
|
||||||
mod restore;
|
mod defer;
|
||||||
|
|
||||||
mod api;
|
mod api;
|
||||||
mod ast;
|
mod ast;
|
||||||
@ -225,6 +225,7 @@ pub use api::custom_syntax::Expression;
|
|||||||
pub use api::files::{eval_file, run_file};
|
pub use api::files::{eval_file, run_file};
|
||||||
pub use api::{eval::eval, events::VarDefInfo, run::run};
|
pub use api::{eval::eval, events::VarDefInfo, run::run};
|
||||||
pub use ast::{FnAccess, AST};
|
pub use ast::{FnAccess, AST};
|
||||||
|
use defer::Deferred;
|
||||||
pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
pub use engine::{Engine, OP_CONTAINS, OP_EQUALS};
|
||||||
pub use eval::EvalContext;
|
pub use eval::EvalContext;
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -233,7 +234,6 @@ use func::calc_typed_method_hash;
|
|||||||
use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash};
|
use func::{calc_fn_hash, calc_fn_hash_full, calc_var_hash};
|
||||||
pub use func::{plugin, FuncArgs, NativeCallContext, RegisterNativeFunction};
|
pub use func::{plugin, FuncArgs, NativeCallContext, RegisterNativeFunction};
|
||||||
pub use module::{FnNamespace, Module};
|
pub use module::{FnNamespace, Module};
|
||||||
use restore::RestoreOnDrop;
|
|
||||||
pub use rhai_codegen::*;
|
pub use rhai_codegen::*;
|
||||||
#[cfg(not(feature = "no_time"))]
|
#[cfg(not(feature = "no_time"))]
|
||||||
pub use types::Instant;
|
pub use types::Instant;
|
||||||
|
@ -195,22 +195,22 @@ pub struct Module {
|
|||||||
/// Custom types.
|
/// Custom types.
|
||||||
custom_types: Option<Box<CustomTypesCollection>>,
|
custom_types: Option<Box<CustomTypesCollection>>,
|
||||||
/// Sub-modules.
|
/// Sub-modules.
|
||||||
modules: Option<Box<BTreeMap<Identifier, SharedModule>>>,
|
modules: Option<BTreeMap<Identifier, SharedModule>>,
|
||||||
/// [`Module`] variables.
|
/// [`Module`] variables.
|
||||||
variables: Option<Box<BTreeMap<Identifier, Dynamic>>>,
|
variables: Option<BTreeMap<Identifier, Dynamic>>,
|
||||||
/// Flattened collection of all [`Module`] variables, including those in sub-modules.
|
/// Flattened collection of all [`Module`] variables, including those in sub-modules.
|
||||||
all_variables: Option<Box<StraightHashMap<Dynamic>>>,
|
all_variables: Option<StraightHashMap<Dynamic>>,
|
||||||
/// Functions (both native Rust and scripted).
|
/// Functions (both native Rust and scripted).
|
||||||
functions: Option<StraightHashMap<FuncInfo>>,
|
functions: Option<StraightHashMap<FuncInfo>>,
|
||||||
/// Flattened collection of all functions, native Rust and scripted.
|
/// Flattened collection of all functions, native Rust and scripted.
|
||||||
/// including those in sub-modules.
|
/// including those in sub-modules.
|
||||||
all_functions: Option<Box<StraightHashMap<CallableFunction>>>,
|
all_functions: Option<StraightHashMap<CallableFunction>>,
|
||||||
/// Bloom filter on native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
|
/// Bloom filter on native Rust functions (in scripted hash format) that contain [`Dynamic`] parameters.
|
||||||
dynamic_functions_filter: Option<Box<BloomFilterU64>>,
|
dynamic_functions_filter: Option<Box<BloomFilterU64>>,
|
||||||
/// Iterator functions, keyed by the type producing the iterator.
|
/// Iterator functions, keyed by the type producing the iterator.
|
||||||
type_iterators: Option<Box<BTreeMap<TypeId, Shared<IteratorFn>>>>,
|
type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
|
||||||
/// Flattened collection of iterator functions, including those in sub-modules.
|
/// Flattened collection of iterator functions, including those in sub-modules.
|
||||||
all_type_iterators: Option<Box<BTreeMap<TypeId, Shared<IteratorFn>>>>,
|
all_type_iterators: Option<BTreeMap<TypeId, Shared<IteratorFn>>>,
|
||||||
/// Flags.
|
/// Flags.
|
||||||
pub(crate) flags: ModuleFlags,
|
pub(crate) flags: ModuleFlags,
|
||||||
}
|
}
|
||||||
@ -234,7 +234,7 @@ impl fmt::Debug for Module {
|
|||||||
"modules",
|
"modules",
|
||||||
&self
|
&self
|
||||||
.modules
|
.modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flat_map(BTreeMap::keys)
|
.flat_map(BTreeMap::keys)
|
||||||
.map(SmartString::as_str)
|
.map(SmartString::as_str)
|
||||||
@ -561,23 +561,23 @@ impl Module {
|
|||||||
.functions
|
.functions
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map_or(true, StraightHashMap::is_empty)
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
&& self.variables.as_deref().map_or(true, BTreeMap::is_empty)
|
&& self.variables.as_ref().map_or(true, BTreeMap::is_empty)
|
||||||
&& self.modules.as_deref().map_or(true, BTreeMap::is_empty)
|
&& self.modules.as_ref().map_or(true, BTreeMap::is_empty)
|
||||||
&& self
|
&& self
|
||||||
.type_iterators
|
.type_iterators
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(true, BTreeMap::is_empty)
|
.map_or(true, BTreeMap::is_empty)
|
||||||
&& self
|
&& self
|
||||||
.all_functions
|
.all_functions
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(true, StraightHashMap::is_empty)
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
&& self
|
&& self
|
||||||
.all_variables
|
.all_variables
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(true, StraightHashMap::is_empty)
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
&& self
|
&& self
|
||||||
.all_type_iterators
|
.all_type_iterators
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(true, BTreeMap::is_empty)
|
.map_or(true, BTreeMap::is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1979,9 +1979,9 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn count(&self) -> (usize, usize, usize) {
|
pub fn count(&self) -> (usize, usize, usize) {
|
||||||
(
|
(
|
||||||
self.variables.as_deref().map_or(0, BTreeMap::len),
|
self.variables.as_ref().map_or(0, BTreeMap::len),
|
||||||
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
||||||
self.type_iterators.as_deref().map_or(0, BTreeMap::len),
|
self.type_iterators.as_ref().map_or(0, BTreeMap::len),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1989,7 +1989,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
pub fn iter_sub_modules(&self) -> impl Iterator<Item = (&str, &SharedModule)> {
|
||||||
self.modules
|
self.modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|(k, m)| (k.as_str(), m))
|
.map(|(k, m)| (k.as_str(), m))
|
||||||
@ -1999,7 +1999,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
pub fn iter_var(&self) -> impl Iterator<Item = (&str, &Dynamic)> {
|
||||||
self.variables
|
self.variables
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.map(|(k, v)| (k.as_str(), v))
|
.map(|(k, v)| (k.as_str(), v))
|
||||||
@ -2392,7 +2392,7 @@ impl Module {
|
|||||||
|
|
||||||
if !self.is_indexed() {
|
if !self.is_indexed() {
|
||||||
let mut path = Vec::with_capacity(4);
|
let mut path = Vec::with_capacity(4);
|
||||||
let mut variables = new_hash_map(self.variables.as_deref().map_or(0, BTreeMap::len));
|
let mut variables = new_hash_map(self.variables.as_ref().map_or(0, BTreeMap::len));
|
||||||
let mut functions =
|
let mut functions =
|
||||||
new_hash_map(self.functions.as_ref().map_or(0, StraightHashMap::len));
|
new_hash_map(self.functions.as_ref().map_or(0, StraightHashMap::len));
|
||||||
let mut type_iterators = BTreeMap::new();
|
let mut type_iterators = BTreeMap::new();
|
||||||
|
@ -840,6 +840,22 @@ fn optimize_stmt(stmt: &mut Stmt, state: &mut OptimizerState, preserve_result: b
|
|||||||
// return expr;
|
// return expr;
|
||||||
Stmt::Return(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
Stmt::Return(Some(ref mut expr), ..) => optimize_expr(expr, state, false),
|
||||||
|
|
||||||
|
// Share nothing
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(x) if x.is_empty() => {
|
||||||
|
state.set_dirty();
|
||||||
|
*stmt = Stmt::Noop(Position::NONE);
|
||||||
|
}
|
||||||
|
// Share constants
|
||||||
|
#[cfg(not(feature = "no_closure"))]
|
||||||
|
Stmt::Share(x) => {
|
||||||
|
let len = x.len();
|
||||||
|
x.retain(|(v, _, _)| !state.find_constant(v).is_some());
|
||||||
|
if x.len() != len {
|
||||||
|
state.set_dirty();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// All other statements - skip
|
// All other statements - skip
|
||||||
_ => (),
|
_ => (),
|
||||||
}
|
}
|
||||||
@ -1262,7 +1278,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
if self
|
if self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.any(|(_, m)| m.contains_qualified_fn(hash))
|
.any(|(_, m)| m.contains_qualified_fn(hash))
|
||||||
|
@ -273,7 +273,7 @@ fn collect_fn_metadata(
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
ctx.engine()
|
ctx.engine()
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.flatten()
|
.flatten()
|
||||||
.flat_map(|(_, m)| m.iter_script_fn())
|
.flat_map(|(_, m)| m.iter_script_fn())
|
||||||
|
@ -261,7 +261,7 @@ impl<'e, 's> ParseState<'e, 's> {
|
|||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings.get_with_mapper(
|
self.interned_strings.get_with_mapper(
|
||||||
crate::engine::FN_GET,
|
b'g',
|
||||||
|s| crate::engine::make_getter(s.as_ref()).into(),
|
|s| crate::engine::make_getter(s.as_ref()).into(),
|
||||||
text,
|
text,
|
||||||
)
|
)
|
||||||
@ -276,7 +276,7 @@ impl<'e, 's> ParseState<'e, 's> {
|
|||||||
text: impl AsRef<str> + Into<ImmutableString>,
|
text: impl AsRef<str> + Into<ImmutableString>,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
self.interned_strings.get_with_mapper(
|
self.interned_strings.get_with_mapper(
|
||||||
crate::engine::FN_SET,
|
b's',
|
||||||
|s| crate::engine::make_setter(s.as_ref()).into(),
|
|s| crate::engine::make_setter(s.as_ref()).into(),
|
||||||
text,
|
text,
|
||||||
)
|
)
|
||||||
@ -601,7 +601,7 @@ impl Engine {
|
|||||||
.any(|m| m.as_str() == root)
|
.any(|m| m.as_str() == root)
|
||||||
&& !self
|
&& !self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(root))
|
.map_or(false, |m| m.contains_key(root))
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -676,7 +676,7 @@ impl Engine {
|
|||||||
.any(|m| m.as_str() == root)
|
.any(|m| m.as_str() == root)
|
||||||
&& !self
|
&& !self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(root))
|
.map_or(false, |m| m.contains_key(root))
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -908,7 +908,7 @@ impl Engine {
|
|||||||
let mut settings = settings;
|
let mut settings = settings;
|
||||||
settings.pos = eat_token(input, Token::LeftBracket);
|
settings.pos = eat_token(input, Token::LeftBracket);
|
||||||
|
|
||||||
let mut array = StaticVec::new_const();
|
let mut array = FnArgsVec::new_const();
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
const MISSING_RBRACKET: &str = "to end this array literal";
|
const MISSING_RBRACKET: &str = "to end this array literal";
|
||||||
@ -1501,7 +1501,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Interpolated string
|
// Interpolated string
|
||||||
Token::InterpolatedString(..) => {
|
Token::InterpolatedString(..) => {
|
||||||
let mut segments = StaticVec::<Expr>::new();
|
let mut segments = FnArgsVec::new_const();
|
||||||
let settings = settings.level_up()?;
|
let settings = settings.level_up()?;
|
||||||
|
|
||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
@ -1577,12 +1577,12 @@ impl Engine {
|
|||||||
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key)
|
Token::Custom(key) | Token::Reserved(key) | Token::Identifier(key)
|
||||||
if self
|
if self
|
||||||
.custom_syntax
|
.custom_syntax
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(&**key)) =>
|
.map_or(false, |m| m.contains_key(&**key)) =>
|
||||||
{
|
{
|
||||||
let (key, syntax) = self
|
let (key, syntax) = self
|
||||||
.custom_syntax
|
.custom_syntax
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.and_then(|m| m.get_key_value(&**key))
|
.and_then(|m| m.get_key_value(&**key))
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let (.., pos) = input.next().expect(NEVER_ENDS);
|
let (.., pos) = input.next().expect(NEVER_ENDS);
|
||||||
@ -1888,7 +1888,7 @@ impl Engine {
|
|||||||
.any(|m| m.as_str() == root)
|
.any(|m| m.as_str() == root)
|
||||||
&& !self
|
&& !self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.map_or(false, |m| m.contains_key(root))
|
.map_or(false, |m| m.contains_key(root))
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -2303,7 +2303,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Token::Custom(c) => self
|
Token::Custom(c) => self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.and_then(|m| m.get(&**c))
|
.and_then(|m| m.get(&**c))
|
||||||
.copied()
|
.copied()
|
||||||
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?,
|
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*current_pos))?,
|
||||||
@ -2329,7 +2329,7 @@ impl Engine {
|
|||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Token::Custom(c) => self
|
Token::Custom(c) => self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.and_then(|m| m.get(&**c))
|
.and_then(|m| m.get(&**c))
|
||||||
.copied()
|
.copied()
|
||||||
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?,
|
.ok_or_else(|| PERR::Reserved(c.to_string()).into_err(*next_pos))?,
|
||||||
@ -2434,7 +2434,7 @@ impl Engine {
|
|||||||
Token::Custom(s)
|
Token::Custom(s)
|
||||||
if self
|
if self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_deref()
|
.as_ref()
|
||||||
.and_then(|m| m.get(s.as_str()))
|
.and_then(|m| m.get(s.as_str()))
|
||||||
.map_or(false, Option::is_some) =>
|
.map_or(false, Option::is_some) =>
|
||||||
{
|
{
|
||||||
@ -3612,31 +3612,39 @@ impl Engine {
|
|||||||
|
|
||||||
// Parse type for `this` pointer
|
// Parse type for `this` pointer
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
let ((token, pos), this_type) = match token {
|
let ((token, pos), this_type) = {
|
||||||
Token::StringConstant(s) if input.peek().expect(NEVER_ENDS).0 == Token::Period => {
|
let (next_token, next_pos) = input.peek().expect(NEVER_ENDS);
|
||||||
eat_token(input, Token::Period);
|
|
||||||
let s = match s.as_str() {
|
match token {
|
||||||
"int" => state.get_interned_string(std::any::type_name::<crate::INT>()),
|
Token::StringConstant(s) if next_token == &Token::Period => {
|
||||||
#[cfg(not(feature = "no_float"))]
|
eat_token(input, Token::Period);
|
||||||
"float" => state.get_interned_string(std::any::type_name::<crate::FLOAT>()),
|
let s = match s.as_str() {
|
||||||
_ => state.get_interned_string(*s),
|
"int" => state.get_interned_string(std::any::type_name::<crate::INT>()),
|
||||||
};
|
#[cfg(not(feature = "no_float"))]
|
||||||
(input.next().expect(NEVER_ENDS), Some(s))
|
"float" => state.get_interned_string(std::any::type_name::<crate::FLOAT>()),
|
||||||
|
_ => state.get_interned_string(*s),
|
||||||
|
};
|
||||||
|
(input.next().expect(NEVER_ENDS), Some(s))
|
||||||
|
}
|
||||||
|
Token::StringConstant(..) => {
|
||||||
|
return Err(PERR::MissingToken(
|
||||||
|
Token::Period.into(),
|
||||||
|
"after the type name for 'this'".into(),
|
||||||
|
)
|
||||||
|
.into_err(*next_pos))
|
||||||
|
}
|
||||||
|
Token::Identifier(s) if next_token == &Token::Period => {
|
||||||
|
eat_token(input, Token::Period);
|
||||||
|
let s = match s.as_str() {
|
||||||
|
"int" => state.get_interned_string(std::any::type_name::<crate::INT>()),
|
||||||
|
#[cfg(not(feature = "no_float"))]
|
||||||
|
"float" => state.get_interned_string(std::any::type_name::<crate::FLOAT>()),
|
||||||
|
_ => state.get_interned_string(*s),
|
||||||
|
};
|
||||||
|
(input.next().expect(NEVER_ENDS), Some(s))
|
||||||
|
}
|
||||||
|
_ => ((token, pos), None),
|
||||||
}
|
}
|
||||||
Token::StringConstant(..) => {
|
|
||||||
return Err(PERR::MissingSymbol(".".to_string()).into_err(pos))
|
|
||||||
}
|
|
||||||
Token::Identifier(s) if input.peek().expect(NEVER_ENDS).0 == Token::Period => {
|
|
||||||
eat_token(input, Token::Period);
|
|
||||||
let s = match s.as_str() {
|
|
||||||
"int" => state.get_interned_string(std::any::type_name::<crate::INT>()),
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
|
||||||
"float" => state.get_interned_string(std::any::type_name::<crate::FLOAT>()),
|
|
||||||
_ => state.get_interned_string(*s),
|
|
||||||
};
|
|
||||||
(input.next().expect(NEVER_ENDS), Some(s))
|
|
||||||
}
|
|
||||||
_ => ((token, pos), None),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
let name = match token.into_function_name_for_override() {
|
let name = match token.into_function_name_for_override() {
|
||||||
@ -3921,7 +3929,7 @@ impl Engine {
|
|||||||
let expr = self.parse_expr(&mut input, state, &mut functions, settings)?;
|
let expr = self.parse_expr(&mut input, state, &mut functions, settings)?;
|
||||||
|
|
||||||
#[cfg(feature = "no_function")]
|
#[cfg(feature = "no_function")]
|
||||||
assert!(functions.is_empty());
|
debug_assert!(functions.is_empty());
|
||||||
|
|
||||||
match input.peek().expect(NEVER_ENDS) {
|
match input.peek().expect(NEVER_ENDS) {
|
||||||
(Token::EOF, ..) => (),
|
(Token::EOF, ..) => (),
|
||||||
|
@ -182,7 +182,7 @@ pub fn gen_metadata_to_json(
|
|||||||
let mut global = ModuleMetadata::new();
|
let mut global = ModuleMetadata::new();
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
for (name, m) in engine.global_sub_modules.as_deref().into_iter().flatten() {
|
for (name, m) in engine.global_sub_modules.as_ref().into_iter().flatten() {
|
||||||
global.modules.insert(name, m.as_ref().into());
|
global.modules.insert(name, m.as_ref().into());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
240
src/tokenizer.rs
240
src/tokenizer.rs
@ -514,117 +514,117 @@ static KEYWORDS_LIST: [(&str, Token); 153] = [
|
|||||||
const MIN_RESERVED_LEN: usize = 1;
|
const MIN_RESERVED_LEN: usize = 1;
|
||||||
const MAX_RESERVED_LEN: usize = 10;
|
const MAX_RESERVED_LEN: usize = 10;
|
||||||
const MIN_RESERVED_HASH_VALUE: usize = 1;
|
const MIN_RESERVED_HASH_VALUE: usize = 1;
|
||||||
const MAX_RESERVED_HASH_VALUE: usize = 112;
|
const MAX_RESERVED_HASH_VALUE: usize = 149;
|
||||||
|
|
||||||
static RESERVED_ASSOC_VALUES: [u8; 256] = [
|
static RESERVED_ASSOC_VALUES: [u8; 256] = [
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 35, 113, 45, 25, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 10, 150, 5, 35, 150, 150,
|
||||||
113, 113, 60, 55, 50, 50, 113, 15, 0, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 45, 35, 30, 30, 150, 20, 15, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 35,
|
||||||
10, 85, 45, 5, 55, 50, 5, 113, 113, 113, 113, 113, 85, 113, 113, 113, 113, 113, 113, 113, 113,
|
30, 15, 5, 25, 0, 25, 150, 150, 150, 150, 150, 65, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 35, 113, 113, 113, 55, 113, 10, 40,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 40, 150, 150, 150, 150, 150, 0, 150, 0,
|
||||||
5, 0, 5, 35, 10, 5, 0, 113, 113, 20, 25, 5, 45, 0, 113, 0, 0, 0, 15, 30, 20, 25, 20, 113, 113,
|
0, 0, 15, 45, 10, 15, 150, 150, 35, 25, 10, 50, 0, 150, 5, 0, 15, 0, 5, 25, 45, 15, 150, 150,
|
||||||
20, 113, 0, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
25, 150, 20, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113, 113,
|
150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150, 150,
|
||||||
];
|
];
|
||||||
static RESERVED_LIST: [(&str, bool, bool, bool); 113] = [
|
static RESERVED_LIST: [(&str, bool, bool, bool); 150] = [
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("~", true, false, false),
|
("?", true, false, false),
|
||||||
("is", true, false, false),
|
|
||||||
("...", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("print", true, true, false),
|
|
||||||
("@", true, false, false),
|
|
||||||
("private", cfg!(feature = "no_function"), false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("this", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("thread", true, false, false),
|
|
||||||
("as", cfg!(feature = "no_module"), false, false),
|
("as", cfg!(feature = "no_module"), false, false),
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("spawn", true, false, false),
|
|
||||||
("static", true, false, false),
|
|
||||||
(":=", true, false, false),
|
|
||||||
("===", true, false, false),
|
|
||||||
("case", true, false, false),
|
|
||||||
("super", true, false, false),
|
|
||||||
("shared", true, false, false),
|
|
||||||
("package", true, false, false),
|
|
||||||
("use", true, false, false),
|
("use", true, false, false),
|
||||||
("with", true, false, false),
|
("case", true, false, false),
|
||||||
("curry", true, true, true),
|
|
||||||
("$", true, false, false),
|
|
||||||
("type_of", true, true, true),
|
|
||||||
("nil", true, false, false),
|
|
||||||
("sync", true, false, false),
|
|
||||||
("yield", true, false, false),
|
|
||||||
("import", cfg!(feature = "no_module"), false, false),
|
|
||||||
("--", true, false, false),
|
|
||||||
("new", true, false, false),
|
|
||||||
("exit", true, false, false),
|
|
||||||
("async", true, false, false),
|
("async", true, false, false),
|
||||||
("export", cfg!(feature = "no_module"), false, false),
|
("public", true, false, false),
|
||||||
("!.", true, false, false),
|
("package", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("call", true, true, true),
|
|
||||||
("match", true, false, false),
|
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("fn", cfg!(feature = "no_function"), false, false),
|
("super", true, false, false),
|
||||||
("var", true, false, false),
|
|
||||||
("null", true, false, false),
|
|
||||||
("await", true, false, false),
|
|
||||||
("#", true, false, false),
|
("#", true, false, false),
|
||||||
|
("private", cfg!(feature = "no_function"), false, false),
|
||||||
|
("var", true, false, false),
|
||||||
|
("protected", true, false, false),
|
||||||
|
("spawn", true, false, false),
|
||||||
|
("shared", true, false, false),
|
||||||
|
("is", true, false, false),
|
||||||
|
("===", true, false, false),
|
||||||
|
("sync", true, false, false),
|
||||||
|
("curry", true, true, true),
|
||||||
|
("static", true, false, false),
|
||||||
("default", true, false, false),
|
("default", true, false, false),
|
||||||
("!==", true, false, false),
|
("!==", true, false, false),
|
||||||
("eval", true, true, false),
|
("is_shared", cfg!(not(feature = "no_closure")), true, true),
|
||||||
("debug", true, true, false),
|
("print", true, true, false),
|
||||||
("?", true, false, false),
|
("", false, false, false),
|
||||||
|
("#!", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("this", true, false, false),
|
||||||
|
("is_def_var", true, true, false),
|
||||||
|
("thread", true, false, false),
|
||||||
("?.", cfg!(feature = "no_object"), false, false),
|
("?.", cfg!(feature = "no_object"), false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("protected", true, false, false),
|
("is_def_fn", cfg!(not(feature = "no_function")), true, false),
|
||||||
|
("yield", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("fn", cfg!(feature = "no_function"), false, false),
|
||||||
|
("new", true, false, false),
|
||||||
|
("call", true, true, true),
|
||||||
|
("match", true, false, false),
|
||||||
|
("~", true, false, false),
|
||||||
|
("!.", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("eval", true, true, false),
|
||||||
|
("await", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
(":=", true, false, false),
|
||||||
|
("...", true, false, false),
|
||||||
|
("null", true, false, false),
|
||||||
|
("debug", true, true, false),
|
||||||
|
("@", true, false, false),
|
||||||
|
("type_of", true, true, true),
|
||||||
|
("", false, false, false),
|
||||||
|
("with", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("go", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("goto", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("public", true, false, false),
|
|
||||||
("<-", true, false, false),
|
("<-", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("is_def_fn", cfg!(not(feature = "no_function")), true, false),
|
("void", true, false, false),
|
||||||
("is_def_var", true, true, false),
|
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
|
("import", cfg!(feature = "no_module"), false, false),
|
||||||
|
("--", true, false, false),
|
||||||
|
("nil", true, false, false),
|
||||||
|
("exit", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("export", cfg!(feature = "no_module"), false, false),
|
||||||
("<|", true, false, false),
|
("<|", true, false, false),
|
||||||
("::<", true, false, false),
|
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
|
("$", true, false, false),
|
||||||
("->", true, false, false),
|
("->", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("module", true, false, false),
|
("", false, false, false),
|
||||||
("|>", true, false, false),
|
("|>", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("void", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("#!", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
|
("module", true, false, false),
|
||||||
("?[", cfg!(feature = "no_index"), false, false),
|
("?[", cfg!(feature = "no_index"), false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("Fn", true, true, false),
|
("Fn", true, true, false),
|
||||||
|
("::<", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("++", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
@ -634,17 +634,54 @@ static RESERVED_LIST: [(&str, bool, bool, bool); 113] = [
|
|||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("++", true, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("", false, false, false),
|
|
||||||
("*)", true, false, false),
|
("*)", true, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("", false, false, false),
|
("", false, false, false),
|
||||||
("(*", true, false, false),
|
("(*", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("go", true, false, false),
|
||||||
|
("", false, false, false),
|
||||||
|
("goto", true, false, false),
|
||||||
];
|
];
|
||||||
|
|
||||||
impl Token {
|
impl Token {
|
||||||
@ -875,12 +912,13 @@ impl Token {
|
|||||||
// by GNU `gperf` on the list of keywords.
|
// by GNU `gperf` on the list of keywords.
|
||||||
let utf8 = syntax.as_bytes();
|
let utf8 = syntax.as_bytes();
|
||||||
let len = utf8.len();
|
let len = utf8.len();
|
||||||
let mut hash_val = len;
|
|
||||||
|
|
||||||
if !(MIN_KEYWORD_LEN..=MAX_KEYWORD_LEN).contains(&len) {
|
if !(MIN_KEYWORD_LEN..=MAX_KEYWORD_LEN).contains(&len) {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let mut hash_val = len;
|
||||||
|
|
||||||
match len {
|
match len {
|
||||||
1 => (),
|
1 => (),
|
||||||
_ => hash_val += KEYWORD_ASSOC_VALUES[(utf8[1] as usize) + 1] as usize,
|
_ => hash_val += KEYWORD_ASSOC_VALUES[(utf8[1] as usize) + 1] as usize,
|
||||||
@ -1219,12 +1257,12 @@ pub fn parse_string_literal(
|
|||||||
ch
|
ch
|
||||||
}
|
}
|
||||||
None if verbatim => {
|
None if verbatim => {
|
||||||
assert_eq!(escape, "", "verbatim strings should not have any escapes");
|
debug_assert_eq!(escape, "", "verbatim strings should not have any escapes");
|
||||||
pos.advance();
|
pos.advance();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
None if allow_line_continuation && !escape.is_empty() => {
|
None if allow_line_continuation && !escape.is_empty() => {
|
||||||
assert_eq!(escape, "\\", "unexpected escape {escape} at end of line");
|
debug_assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape);
|
||||||
pos.advance();
|
pos.advance();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@ -1343,14 +1381,14 @@ pub fn parse_string_literal(
|
|||||||
|
|
||||||
// Verbatim
|
// Verbatim
|
||||||
'\n' if verbatim => {
|
'\n' if verbatim => {
|
||||||
assert_eq!(escape, "", "verbatim strings should not have any escapes");
|
debug_assert_eq!(escape, "", "verbatim strings should not have any escapes");
|
||||||
pos.new_line();
|
pos.new_line();
|
||||||
result.push(next_char);
|
result.push(next_char);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Line continuation
|
// Line continuation
|
||||||
'\n' if allow_line_continuation && !escape.is_empty() => {
|
'\n' if allow_line_continuation && !escape.is_empty() => {
|
||||||
assert_eq!(escape, "\\", "unexpected escape {escape} at end of line");
|
debug_assert_eq!(escape, "\\", "unexpected escape {} at end of line", escape);
|
||||||
escape.clear();
|
escape.clear();
|
||||||
pos.new_line();
|
pos.new_line();
|
||||||
|
|
||||||
@ -2306,8 +2344,10 @@ pub fn is_id_continue(x: char) -> bool {
|
|||||||
/// The first `bool` indicates whether it is a reserved keyword or symbol.
|
/// The first `bool` indicates whether it is a reserved keyword or symbol.
|
||||||
///
|
///
|
||||||
/// The second `bool` indicates whether the keyword can be called normally as a function.
|
/// The second `bool` indicates whether the keyword can be called normally as a function.
|
||||||
|
/// `false` if it is not a reserved keyword.
|
||||||
///
|
///
|
||||||
/// The third `bool` indicates whether the keyword can be called in method-call style.
|
/// The third `bool` indicates whether the keyword can be called in method-call style.
|
||||||
|
/// `false` if it is not a reserved keyword or it cannot be called as a function.
|
||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) {
|
pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) {
|
||||||
@ -2315,16 +2355,19 @@ pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) {
|
|||||||
// by GNU `gperf` on the list of keywords.
|
// by GNU `gperf` on the list of keywords.
|
||||||
let utf8 = syntax.as_bytes();
|
let utf8 = syntax.as_bytes();
|
||||||
let len = utf8.len();
|
let len = utf8.len();
|
||||||
let rounds = len.min(3);
|
|
||||||
let mut hash_val = len;
|
|
||||||
|
|
||||||
if !(MIN_RESERVED_LEN..=MAX_RESERVED_LEN).contains(&len) {
|
if !(MIN_RESERVED_LEN..=MAX_RESERVED_LEN).contains(&len) {
|
||||||
return (false, false, false);
|
return (false, false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
for x in 0..rounds {
|
let mut hash_val = len;
|
||||||
hash_val += RESERVED_ASSOC_VALUES[utf8[rounds - 1 - x] as usize] as usize;
|
|
||||||
|
match len {
|
||||||
|
1 => (),
|
||||||
|
_ => hash_val += RESERVED_ASSOC_VALUES[utf8[1] as usize] as usize,
|
||||||
}
|
}
|
||||||
|
hash_val += RESERVED_ASSOC_VALUES[utf8[0] as usize] as usize;
|
||||||
|
hash_val += RESERVED_ASSOC_VALUES[utf8[len - 1] as usize] as usize;
|
||||||
|
|
||||||
if !(MIN_RESERVED_HASH_VALUE..=MAX_RESERVED_HASH_VALUE).contains(&hash_val) {
|
if !(MIN_RESERVED_HASH_VALUE..=MAX_RESERVED_HASH_VALUE).contains(&hash_val) {
|
||||||
return (false, false, false);
|
return (false, false, false);
|
||||||
@ -2332,13 +2375,12 @@ pub fn is_reserved_keyword_or_symbol(syntax: &str) -> (bool, bool, bool) {
|
|||||||
|
|
||||||
match RESERVED_LIST[hash_val] {
|
match RESERVED_LIST[hash_val] {
|
||||||
("", ..) => (false, false, false),
|
("", ..) => (false, false, false),
|
||||||
(s, true, a, b) => (
|
(s, true, a, b) => {
|
||||||
// Fail early to avoid calling memcmp().
|
// Fail early to avoid calling memcmp().
|
||||||
// Since we are already working with bytes, mind as well check the first one.
|
// Since we are already working with bytes, mind as well check the first one.
|
||||||
s.len() == len && s.as_bytes()[0] == utf8[0] && s == syntax,
|
let is_reserved = s.len() == len && s.as_bytes()[0] == utf8[0] && s == syntax;
|
||||||
a,
|
(is_reserved, is_reserved && a, is_reserved && a && b)
|
||||||
b,
|
}
|
||||||
),
|
|
||||||
_ => (false, false, false),
|
_ => (false, false, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2450,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_deref().map_or(false, |m| m.contains_key(&*s)),
|
self.engine.custom_keywords.as_ref().map_or(false, |m| m.contains_key(&*s)),
|
||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
@ -2487,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_deref().map_or(false,|m| m.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"});
|
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())
|
||||||
},
|
},
|
||||||
@ -2496,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_deref().map_or(false,|m| m.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)
|
(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_deref().map_or(false,|m| m.contains_key(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_deref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(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 {
|
||||||
@ -2511,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_deref().map_or(false,|m| m.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)
|
(Token::Reserved(Box::new(token.literal_syntax().into())), pos)
|
||||||
}
|
}
|
||||||
// Normal symbol
|
// Normal symbol
|
||||||
|
@ -62,7 +62,7 @@ impl StringsInterner {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get<S: AsRef<str> + Into<ImmutableString>>(&mut self, text: S) -> ImmutableString {
|
pub fn get<S: AsRef<str> + Into<ImmutableString>>(&mut self, text: S) -> ImmutableString {
|
||||||
self.get_with_mapper("", Into::into, text)
|
self.get_with_mapper(0, Into::into, text)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get an identifier from a text string, adding it to the interner if necessary.
|
/// Get an identifier from a text string, adding it to the interner if necessary.
|
||||||
@ -70,19 +70,19 @@ impl StringsInterner {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_with_mapper<S: AsRef<str>>(
|
pub fn get_with_mapper<S: AsRef<str>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
category: &str,
|
category: u8,
|
||||||
mapper: impl FnOnce(S) -> ImmutableString,
|
mapper: impl FnOnce(S) -> ImmutableString,
|
||||||
text: S,
|
text: S,
|
||||||
) -> ImmutableString {
|
) -> ImmutableString {
|
||||||
let key = text.as_ref();
|
let key = text.as_ref();
|
||||||
|
|
||||||
let hasher = &mut get_hasher();
|
let hasher = &mut get_hasher();
|
||||||
category.hash(hasher);
|
hasher.write_u8(category);
|
||||||
key.hash(hasher);
|
key.hash(hasher);
|
||||||
let hash = hasher.finish();
|
let hash = hasher.finish();
|
||||||
|
|
||||||
// Cache long strings only on the second try to avoid caching "one-hit wonders".
|
// Do not cache long strings and avoid caching "one-hit wonders".
|
||||||
if key.len() > MAX_STRING_LEN && self.bloom_filter.is_absent_and_set(hash) {
|
if key.len() > MAX_STRING_LEN || self.bloom_filter.is_absent_and_set(hash) {
|
||||||
return mapper(text);
|
return mapper(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -108,6 +108,15 @@ fn test_method_call_typed() -> Result<(), Box<EvalAltResult>> {
|
|||||||
TestStruct { x: 1002 }
|
TestStruct { x: 1002 }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
assert!(engine.eval::<bool>(
|
||||||
|
r#"
|
||||||
|
fn "Test-Struct#ABC".foo(x) {
|
||||||
|
this.update(x);
|
||||||
|
}
|
||||||
|
is_def_fn("Test-Struct#ABC", "foo", 1)
|
||||||
|
"#
|
||||||
|
)?);
|
||||||
|
|
||||||
assert!(matches!(
|
assert!(matches!(
|
||||||
*engine
|
*engine
|
||||||
.run(
|
.run(
|
||||||
|
@ -3,7 +3,7 @@
|
|||||||
//
|
//
|
||||||
// Generate the output table via:
|
// Generate the output table via:
|
||||||
// ```bash
|
// ```bash
|
||||||
// gperf -t keywords.txt
|
// gperf keywords.txt
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// Since GNU gperf does not produce Rust output, the ANSI-C output must be hand-edited and
|
// Since GNU gperf does not produce Rust output, the ANSI-C output must be hand-edited and
|
||||||
@ -17,86 +17,89 @@
|
|||||||
// * Copy the `wordlist` array into `KEYWORDS_LIST` with the following modifications:
|
// * Copy the `wordlist` array into `KEYWORDS_LIST` with the following modifications:
|
||||||
// - Remove the `#line` comments
|
// - Remove the `#line` comments
|
||||||
// - Change the entry wrapping `{ .. }` into tuples `( .. )`
|
// - Change the entry wrapping `{ .. }` into tuples `( .. )`
|
||||||
// - Replace all entries `("")` by `("", Token::EOF)`
|
|
||||||
// - Put feature flags on the appropriate lines, and duplicating lines that maps to `Token::EOF`
|
// - Put feature flags on the appropriate lines, and duplicating lines that maps to `Token::EOF`
|
||||||
// for the opposite feature flags
|
// for the opposite feature flags
|
||||||
//
|
//
|
||||||
|
%global-table
|
||||||
|
%struct-type
|
||||||
|
%omit-struct-type
|
||||||
|
%define initializer-suffix ,Token::EOF
|
||||||
struct keyword;
|
struct keyword;
|
||||||
%%
|
%%
|
||||||
"{", Token::LeftBrace
|
{, Token::LeftBrace
|
||||||
"}", Token::RightBrace
|
}, Token::RightBrace
|
||||||
"(", Token::LeftParen
|
(, Token::LeftParen
|
||||||
")", Token::RightParen
|
), Token::RightParen
|
||||||
"[", Token::LeftBracket
|
[, Token::LeftBracket
|
||||||
"]", Token::RightBracket
|
], Token::RightBracket
|
||||||
"()", Token::Unit
|
(), Token::Unit
|
||||||
"+", Token::Plus
|
+, Token::Plus
|
||||||
"-", Token::Minus
|
-, Token::Minus
|
||||||
"*", Token::Multiply
|
*, Token::Multiply
|
||||||
"/", Token::Divide
|
/, Token::Divide
|
||||||
";", Token::SemiColon
|
;, Token::SemiColon
|
||||||
":", Token::Colon
|
:, Token::Colon
|
||||||
"::", Token::DoubleColon
|
::, Token::DoubleColon
|
||||||
"=>", Token::DoubleArrow
|
=>, Token::DoubleArrow
|
||||||
"_", Token::Underscore
|
_, Token::Underscore
|
||||||
",", Token::Comma
|
,, Token::Comma
|
||||||
".", Token::Period
|
., Token::Period
|
||||||
"?.", Token::Elvis
|
?., Token::Elvis
|
||||||
"??", Token::DoubleQuestion
|
??, Token::DoubleQuestion
|
||||||
"?[", Token::QuestionBracket
|
?[, Token::QuestionBracket
|
||||||
"..", Token::ExclusiveRange
|
.., Token::ExclusiveRange
|
||||||
"..=", Token::InclusiveRange
|
..=, Token::InclusiveRange
|
||||||
"#{", Token::MapStart
|
"#{", Token::MapStart
|
||||||
"=", Token::Equals
|
=, Token::Equals
|
||||||
"true", Token::True
|
true, Token::True
|
||||||
"false", Token::False
|
false, Token::False
|
||||||
"let", Token::Let
|
let, Token::Let
|
||||||
"const", Token::Const
|
const, Token::Const
|
||||||
"if", Token::If
|
if, Token::If
|
||||||
"else", Token::Else
|
else, Token::Else
|
||||||
"switch", Token::Switch
|
switch, Token::Switch
|
||||||
"do", Token::Do
|
do, Token::Do
|
||||||
"while", Token::While
|
while, Token::While
|
||||||
"until", Token::Until
|
until, Token::Until
|
||||||
"loop", Token::Loop
|
loop, Token::Loop
|
||||||
"for", Token::For
|
for, Token::For
|
||||||
"in", Token::In
|
in, Token::In
|
||||||
"!in", Token::NotIn
|
!in, Token::NotIn
|
||||||
"<", Token::LessThan
|
<, Token::LessThan
|
||||||
">", Token::GreaterThan
|
>, Token::GreaterThan
|
||||||
"<=", Token::LessThanEqualsTo
|
<=, Token::LessThanEqualsTo
|
||||||
">=", Token::GreaterThanEqualsTo
|
>=, Token::GreaterThanEqualsTo
|
||||||
"==", Token::EqualsTo
|
==, Token::EqualsTo
|
||||||
"!=", Token::NotEqualsTo
|
!=, Token::NotEqualsTo
|
||||||
"!", Token::Bang
|
!, Token::Bang
|
||||||
"|", Token::Pipe
|
|, Token::Pipe
|
||||||
"||", Token::Or
|
||, Token::Or
|
||||||
"&", Token::Ampersand
|
&, Token::Ampersand
|
||||||
"&&", Token::And
|
&&, Token::And
|
||||||
"continue", Token::Continue
|
continue, Token::Continue
|
||||||
"break", Token::Break
|
break, Token::Break
|
||||||
"return", Token::Return
|
return, Token::Return
|
||||||
"throw", Token::Throw
|
throw, Token::Throw
|
||||||
"try", Token::Try
|
try, Token::Try
|
||||||
"catch", Token::Catch
|
catch, Token::Catch
|
||||||
"+=", Token::PlusAssign
|
+=, Token::PlusAssign
|
||||||
"-=", Token::MinusAssign
|
-=, Token::MinusAssign
|
||||||
"*=", Token::MultiplyAssign
|
*=, Token::MultiplyAssign
|
||||||
"/=", Token::DivideAssign
|
/=, Token::DivideAssign
|
||||||
"<<=", Token::LeftShiftAssign
|
<<=, Token::LeftShiftAssign
|
||||||
">>=", Token::RightShiftAssign
|
>>=, Token::RightShiftAssign
|
||||||
"&=", Token::AndAssign
|
&=, Token::AndAssign
|
||||||
"|=", Token::OrAssign
|
|=, Token::OrAssign
|
||||||
"^=", Token::XOrAssign
|
^=, Token::XOrAssign
|
||||||
"<<", Token::LeftShift
|
<<, Token::LeftShift
|
||||||
">>", Token::RightShift
|
>>, Token::RightShift
|
||||||
"^", Token::XOr
|
^, Token::XOr
|
||||||
"%", Token::Modulo
|
%, Token::Modulo
|
||||||
"%=", Token::ModuloAssign
|
%=, Token::ModuloAssign
|
||||||
"**", Token::PowerOf
|
**, Token::PowerOf
|
||||||
"**=", Token::PowerOfAssign
|
**=, Token::PowerOfAssign
|
||||||
"fn", Token::Fn
|
fn, Token::Fn
|
||||||
"private", Token::Private
|
private, Token::Private
|
||||||
"import", Token::Import
|
import, Token::Import
|
||||||
"export", Token::Export
|
export, Token::Export
|
||||||
"as", Token::As
|
as, Token::As
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
//
|
//
|
||||||
// Generate the output table via:
|
// Generate the output table via:
|
||||||
// ```bash
|
// ```bash
|
||||||
// gperf -t reserved.txt
|
// gperf reserved.txt
|
||||||
// ```
|
// ```
|
||||||
//
|
//
|
||||||
// Since GNU gperf does not produce Rust output, the ANSI-C output must be hand-edited and
|
// Since GNU gperf does not produce Rust output, the ANSI-C output must be hand-edited and
|
||||||
@ -21,73 +21,88 @@
|
|||||||
// * Copy the `wordlist` array into `RESERVED_LIST` with the following modifications:
|
// * Copy the `wordlist` array into `RESERVED_LIST` with the following modifications:
|
||||||
// - Remove the `#line` comments
|
// - Remove the `#line` comments
|
||||||
// - Change the entry wrapping `{ .. }` into tuples `( .. )`
|
// - Change the entry wrapping `{ .. }` into tuples `( .. )`
|
||||||
// - Replace all entries `("")` by `("", false, false, false)`
|
|
||||||
// - Feature flags can be incorporated directly into the output via the `cfg!` macro
|
// - Feature flags can be incorporated directly into the output via the `cfg!` macro
|
||||||
//
|
//
|
||||||
|
%global-table
|
||||||
|
%struct-type
|
||||||
|
%omit-struct-type
|
||||||
|
%define initializer-suffix ,false,false,false
|
||||||
struct reserved;
|
struct reserved;
|
||||||
%%
|
%%
|
||||||
"?.", cfg!(feature = "no_object"), false, false
|
# reserved under certain flags
|
||||||
"?[", cfg!(feature = "no_index"), false, false
|
#
|
||||||
"fn", cfg!(feature = "no_function"), false, false
|
?., cfg!(feature = no_object), false, false
|
||||||
"private", cfg!(feature = "no_function"), false, false
|
?[, cfg!(feature = no_index), false, false
|
||||||
"import", cfg!(feature = "no_module"), false, false
|
fn, cfg!(feature = no_function), false, false
|
||||||
"export", cfg!(feature = "no_module"), false, false
|
private, cfg!(feature = no_function), false, false
|
||||||
"as", cfg!(feature = "no_module"), false, false
|
import, cfg!(feature = no_module), false, false
|
||||||
"===", true, false, false
|
export, cfg!(feature = no_module), false, false
|
||||||
"!==", true, false, false
|
as, cfg!(feature = no_module), false, false
|
||||||
"->", true, false, false
|
#
|
||||||
"<-", true, false, false
|
# reserved symbols
|
||||||
"?", true, false, false
|
#
|
||||||
":=", true, false, false
|
===, true, false, false
|
||||||
":;", true, false, false
|
!==, true, false, false
|
||||||
"~", true, false, false
|
->, true, false, false
|
||||||
"!.", true, false, false
|
<-, true, false, false
|
||||||
"::<", true, false, false
|
?, true, false, false
|
||||||
"(*", true, false, false
|
:=, true, false, false
|
||||||
"*)", true, false, false
|
:;, true, false, false
|
||||||
|
~, true, false, false
|
||||||
|
!., true, false, false
|
||||||
|
::<, true, false, false
|
||||||
|
(*, true, false, false
|
||||||
|
*), true, false, false
|
||||||
"#", true, false, false
|
"#", true, false, false
|
||||||
"#!", true, false, false
|
"#!", true, false, false
|
||||||
"@", true, false, false
|
@, true, false, false
|
||||||
"$", true, false, false
|
$, true, false, false
|
||||||
"++", true, false, false
|
++, true, false, false
|
||||||
"--", true, false, false
|
--, true, false, false
|
||||||
"...", true, false, false
|
..., true, false, false
|
||||||
"<|", true, false, false
|
<|, true, false, false
|
||||||
"|>", true, false, false
|
|>, true, false, false
|
||||||
"public", true, false, false
|
#
|
||||||
"protected", true, false, false
|
# reserved keywords
|
||||||
"super", true, false, false
|
#
|
||||||
"new", true, false, false
|
public, true, false, false
|
||||||
"use", true, false, false
|
protected, true, false, false
|
||||||
"module", true, false, false
|
super, true, false, false
|
||||||
"package", true, false, false
|
new, true, false, false
|
||||||
"var", true, false, false
|
use, true, false, false
|
||||||
"static", true, false, false
|
module, true, false, false
|
||||||
"shared", true, false, false
|
package, true, false, false
|
||||||
"with", true, false, false
|
var, true, false, false
|
||||||
"is", true, false, false
|
static, true, false, false
|
||||||
"goto", true, false, false
|
shared, true, false, false
|
||||||
"exit", true, false, false
|
with, true, false, false
|
||||||
"match", true, false, false
|
is, true, false, false
|
||||||
"case", true, false, false
|
goto, true, false, false
|
||||||
"default", true, false, false
|
exit, true, false, false
|
||||||
"void", true, false, false
|
match, true, false, false
|
||||||
"null", true, false, false
|
case, true, false, false
|
||||||
"nil", true, false, false
|
default, true, false, false
|
||||||
"spawn", true, false, false
|
void, true, false, false
|
||||||
"thread", true, false, false
|
null, true, false, false
|
||||||
"go", true, false, false
|
nil, true, false, false
|
||||||
"sync", true, false, false
|
spawn, true, false, false
|
||||||
"async", true, false, false
|
thread, true, false, false
|
||||||
"await", true, false, false
|
go, true, false, false
|
||||||
"yield", true, false, false
|
sync, true, false, false
|
||||||
"print", true, true, false
|
async, true, false, false
|
||||||
"debug", true, true, false
|
await, true, false, false
|
||||||
"type_of", true, true, true
|
yield, true, false, false
|
||||||
"eval", true, true, false
|
#
|
||||||
"Fn", true, true, false
|
# keyword functions
|
||||||
"call", true, true, true
|
#
|
||||||
"curry", true, true, true
|
print, true, true, false
|
||||||
"this", true, false, false
|
debug, true, true, false
|
||||||
"is_def_var", true, true, false
|
type_of, true, true, true
|
||||||
"is_def_fn", cfg!(not(feature = "no_function")), true, false
|
eval, true, true, false
|
||||||
|
Fn, true, true, false
|
||||||
|
call, true, true, true
|
||||||
|
curry, true, true, true
|
||||||
|
this, true, false, false
|
||||||
|
is_def_var, true, true, false
|
||||||
|
is_def_fn, cfg!(not(feature = no_function)), true, false
|
||||||
|
is_shared, cfg!(not(feature = no_closure)), true, true
|
||||||
|
Loading…
Reference in New Issue
Block a user