More code refactor.
This commit is contained in:
parent
fbe30b8d0e
commit
d645d8271c
@ -14,6 +14,12 @@ Breaking API changes
|
|||||||
|
|
||||||
* The callback for initializing a debugger instance has changed to `Fn(&Engine, Debugger) -> Debugger`. This allows more control over the initial setup of the debugger.
|
* The callback for initializing a debugger instance has changed to `Fn(&Engine, Debugger) -> Debugger`. This allows more control over the initial setup of the debugger.
|
||||||
|
|
||||||
|
Deprecated API's
|
||||||
|
----------------
|
||||||
|
|
||||||
|
* `Module::with_capacity` is deprecated.
|
||||||
|
* The internal method `Engine::eval_statements_raw` is deprecated.
|
||||||
|
|
||||||
Net features
|
Net features
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -101,7 +101,7 @@ impl<'a, T: Variant + Clone> TypeBuilder<'a, T> {
|
|||||||
Self {
|
Self {
|
||||||
engine,
|
engine,
|
||||||
name: None,
|
name: None,
|
||||||
_marker: PhantomData::default(),
|
_marker: PhantomData,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -81,7 +81,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// The [`AST`] is evaluated before calling the function.
|
/// The [`AST`] is evaluated before calling the function.
|
||||||
/// This allows a script to load the necessary modules.
|
/// This allows a script to load the necessary modules.
|
||||||
/// This is usually desired. If not, use [`call_fn_with_options`] instead.
|
/// This is usually desired. If not, use [`call_fn_with_options`][Engine::call_fn_with_options] instead.
|
||||||
///
|
///
|
||||||
/// # Example
|
/// # Example
|
||||||
///
|
///
|
||||||
@ -276,7 +276,7 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
self.run_debugger(global, caches, scope, this_ptr, node)?;
|
self.run_debugger(global, caches, scope, this_ptr, node)?;
|
||||||
|
@ -218,13 +218,10 @@ impl Engine {
|
|||||||
scripts: impl AsRef<[S]>,
|
scripts: impl AsRef<[S]>,
|
||||||
optimization_level: OptimizationLevel,
|
optimization_level: OptimizationLevel,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let (stream, tokenizer_control) = self.lex_raw(
|
let (stream, tc) = self.lex_raw(scripts.as_ref(), self.token_mapper.as_deref());
|
||||||
scripts.as_ref(),
|
|
||||||
self.token_mapper.as_ref().map(<_>::as_ref),
|
|
||||||
);
|
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||||
let mut _ast = self.parse(&mut stream.peekable(), &mut state, optimization_level)?;
|
let mut _ast = self.parse(stream.peekable(), state, optimization_level)?;
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
_ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n"));
|
_ast.set_doc(state.tokenizer_control.borrow().global_comments.join("\n"));
|
||||||
Ok(_ast)
|
Ok(_ast)
|
||||||
@ -292,12 +289,9 @@ impl Engine {
|
|||||||
script: impl AsRef<str>,
|
script: impl AsRef<str>,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let (stream, tokenizer_control) =
|
let (stream, t) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
|
||||||
|
|
||||||
let mut peekable = stream.peekable();
|
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
let state = &mut ParseState::new(scope, interned_strings, t);
|
||||||
self.parse_global_expr(&mut peekable, &mut state, |_| {}, self.optimization_level)
|
self.parse_global_expr(stream.peekable(), state, |_| {}, self.optimization_level)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -259,7 +259,7 @@ 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_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains(s))
|
.map_or(false, |m| m.contains(s))
|
||||||
|| token.map_or(false, |v| v.is_reserved()))
|
|| token.map_or(false, |v| v.is_reserved()))
|
||||||
&& !self
|
&& !self
|
||||||
|
@ -26,7 +26,7 @@ impl Engine {
|
|||||||
///
|
///
|
||||||
/// This method is deprecated. Use [`run_file`][Engine::run_file] instead.
|
/// This method is deprecated. Use [`run_file`][Engine::run_file] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next majocd cr version.
|
/// This method will be removed in the next major version.
|
||||||
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
#[deprecated(since = "1.1.0", note = "use `run_file` instead")]
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_family = "wasm"))]
|
#[cfg(not(target_family = "wasm"))]
|
||||||
@ -137,12 +137,6 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
/// Call a script function defined in an [`AST`] with multiple [`Dynamic`] arguments.
|
||||||
///
|
///
|
||||||
/// The following options are available:
|
|
||||||
///
|
|
||||||
/// * whether to evaluate the [`AST`] to load necessary modules before calling the function
|
|
||||||
/// * whether to rewind the [`Scope`] after the function call
|
|
||||||
/// * a value for binding to the `this` pointer (if any)
|
|
||||||
///
|
|
||||||
/// Not available under `no_function`.
|
/// Not available under `no_function`.
|
||||||
///
|
///
|
||||||
/// # Deprecated
|
/// # Deprecated
|
||||||
@ -253,12 +247,6 @@ impl Engine {
|
|||||||
/// This method is deprecated. Use [`register_indexer_get`][Engine::register_indexer_get] instead.
|
/// This method is deprecated. Use [`register_indexer_get`][Engine::register_indexer_get] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
|
|
||||||
/// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
|
|
||||||
/// Indexers for arrays, object maps, strings and integers cannot be registered.
|
|
||||||
#[deprecated(since = "1.9.1", note = "use `register_indexer_get` instead")]
|
#[deprecated(since = "1.9.1", note = "use `register_indexer_get` instead")]
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -284,12 +272,6 @@ impl Engine {
|
|||||||
/// This method is deprecated. Use [`register_indexer_set`][Engine::register_indexer_set] instead.
|
/// This method is deprecated. Use [`register_indexer_set`][Engine::register_indexer_set] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
///
|
|
||||||
/// # Panics
|
|
||||||
///
|
|
||||||
/// Panics if the type is [`Array`][crate::Array], [`Map`][crate::Map], [`String`],
|
|
||||||
/// [`ImmutableString`][crate::ImmutableString], `&str` or [`INT`][crate::INT].
|
|
||||||
/// Indexers for arrays, object maps, strings and integers cannot be registered.
|
|
||||||
#[deprecated(since = "1.9.1", note = "use `register_indexer_set` instead")]
|
#[deprecated(since = "1.9.1", note = "use `register_indexer_set` instead")]
|
||||||
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
@ -316,34 +298,6 @@ impl Engine {
|
|||||||
/// Use [`register_custom_syntax_with_state_raw`][Engine::register_custom_syntax_with_state_raw] instead.
|
/// Use [`register_custom_syntax_with_state_raw`][Engine::register_custom_syntax_with_state_raw] instead.
|
||||||
///
|
///
|
||||||
/// This method will be removed in the next major version.
|
/// This method will be removed in the next major version.
|
||||||
///
|
|
||||||
/// # WARNING - Low Level API
|
|
||||||
///
|
|
||||||
/// This function is very low level.
|
|
||||||
///
|
|
||||||
/// * `scope_may_be_changed` specifies variables have been added/removed by this custom syntax.
|
|
||||||
/// * `parse` is the parsing function.
|
|
||||||
/// * `func` is the implementation function.
|
|
||||||
///
|
|
||||||
/// All custom keywords used as symbols must be manually registered via [`Engine::register_custom_operator`].
|
|
||||||
/// Otherwise, they won't be recognized.
|
|
||||||
///
|
|
||||||
/// # Parsing Function Signature
|
|
||||||
///
|
|
||||||
/// The parsing function has the following signature:
|
|
||||||
///
|
|
||||||
/// `Fn(symbols: &[ImmutableString], look_ahead: &str) -> Result<Option<ImmutableString>, ParseError>`
|
|
||||||
///
|
|
||||||
/// where:
|
|
||||||
/// * `symbols`: a slice of symbols that have been parsed so far, possibly containing `$expr$` and/or `$block$`;
|
|
||||||
/// `$ident$` and other literal markers are replaced by the actual text
|
|
||||||
/// * `look_ahead`: a string slice containing the next symbol that is about to be read
|
|
||||||
///
|
|
||||||
/// ## Return value
|
|
||||||
///
|
|
||||||
/// * `Ok(None)`: parsing complete and there are no more symbols to match.
|
|
||||||
/// * `Ok(Some(symbol))`: the next symbol to match, which can also be `$expr$`, `$ident$` or `$block$`.
|
|
||||||
/// * `Err(ParseError)`: error that is reflected back to the [`Engine`], normally `ParseError(ParseErrorType::BadInput(LexError::ImproperSymbol(message)), Position::NONE)` to indicate a syntax error, but it can be any [`ParseError`][crate::ParseError].
|
|
||||||
#[deprecated(
|
#[deprecated(
|
||||||
since = "1.11.0",
|
since = "1.11.0",
|
||||||
note = "use `register_custom_syntax_with_state_raw` instead"
|
note = "use `register_custom_syntax_with_state_raw` instead"
|
||||||
@ -368,6 +322,24 @@ impl Engine {
|
|||||||
move |context, expressions, _| func(context, expressions),
|
move |context, expressions, _| func(context, expressions),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
/// _(internals)_ Evaluate a list of statements with no `this` pointer.
|
||||||
|
/// Exported under the `internals` feature only.
|
||||||
|
///
|
||||||
|
/// # Deprecated
|
||||||
|
///
|
||||||
|
/// This method is deprecated. It will be removed in the next major version.
|
||||||
|
#[cfg(feature = "internals")]
|
||||||
|
#[inline(always)]
|
||||||
|
#[deprecated(since = "1.12.0")]
|
||||||
|
pub fn eval_statements_raw(
|
||||||
|
&self,
|
||||||
|
global: &mut crate::eval::GlobalRuntimeState,
|
||||||
|
caches: &mut crate::eval::Caches,
|
||||||
|
scope: &mut Scope,
|
||||||
|
statements: &[crate::ast::Stmt],
|
||||||
|
) -> RhaiResult {
|
||||||
|
self.eval_global_statements(global, caches, scope, statements)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Dynamic {
|
impl Dynamic {
|
||||||
|
@ -121,15 +121,14 @@ impl Engine {
|
|||||||
let ast = {
|
let ast = {
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
|
|
||||||
let (stream, tokenizer_control) =
|
let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
|
||||||
|
|
||||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||||
|
|
||||||
// No need to optimize a lone expression
|
// No need to optimize a lone expression
|
||||||
self.parse_global_expr(
|
self.parse_global_expr(
|
||||||
&mut stream.peekable(),
|
stream.peekable(),
|
||||||
&mut state,
|
state,
|
||||||
|_| {},
|
|_| {},
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
@ -243,7 +242,7 @@ impl Engine {
|
|||||||
let result = self.eval_global_statements(global, caches, scope, statements);
|
let result = self.eval_global_statements(global, caches, scope, statements);
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
let mut this = Dynamic::NULL;
|
let mut this = Dynamic::NULL;
|
||||||
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
let node = &crate::ast::Stmt::Noop(Position::NONE);
|
||||||
@ -263,25 +262,6 @@ impl Engine {
|
|||||||
|
|
||||||
result
|
result
|
||||||
}
|
}
|
||||||
/// _(internals)_ Evaluate a list of statements with no `this` pointer.
|
|
||||||
/// Exported under the `internals` feature only.
|
|
||||||
///
|
|
||||||
/// This is commonly used to evaluate a list of statements in an [`AST`] or a script function body.
|
|
||||||
///
|
|
||||||
/// # WARNING - Low Level API
|
|
||||||
///
|
|
||||||
/// This function is very low level.
|
|
||||||
#[cfg(feature = "internals")]
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn eval_statements_raw(
|
|
||||||
&self,
|
|
||||||
global: &mut GlobalRuntimeState,
|
|
||||||
caches: &mut Caches,
|
|
||||||
scope: &mut Scope,
|
|
||||||
statements: &[crate::ast::Stmt],
|
|
||||||
) -> RhaiResult {
|
|
||||||
self.eval_global_statements(global, caches, scope, statements)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Evaluate a string as a script, returning the result value or an error.
|
/// Evaluate a string as a script, returning the result value or an error.
|
||||||
|
@ -362,7 +362,7 @@ impl Engine {
|
|||||||
+ SendSync
|
+ SendSync
|
||||||
+ 'static,
|
+ 'static,
|
||||||
) -> &mut Self {
|
) -> &mut Self {
|
||||||
self.debugger = Some(Box::new((Box::new(init), Box::new(callback))));
|
self.debugger_interface = Some(Box::new((Box::new(init), Box::new(callback))));
|
||||||
self
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -120,11 +120,11 @@ impl Engine {
|
|||||||
let ast = {
|
let ast = {
|
||||||
let scope = Scope::new();
|
let scope = Scope::new();
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
let mut state = ParseState::new(&scope, interned_strings, tokenizer_control);
|
let state = &mut ParseState::new(&scope, interned_strings, tokenizer_control);
|
||||||
|
|
||||||
self.parse_global_expr(
|
self.parse_global_expr(
|
||||||
&mut stream.peekable(),
|
stream.peekable(),
|
||||||
&mut state,
|
state,
|
||||||
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
|
|s| s.flags |= ParseSettingFlags::DISALLOW_UNQUOTED_MAP_PROPERTIES,
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
OptimizationLevel::None,
|
OptimizationLevel::None,
|
||||||
|
@ -1,7 +1,5 @@
|
|||||||
//! Module defining the public API of the Rhai engine.
|
//! Module defining the public API of the Rhai engine.
|
||||||
|
|
||||||
pub mod type_names;
|
|
||||||
|
|
||||||
pub mod eval;
|
pub mod eval;
|
||||||
|
|
||||||
pub mod run;
|
pub mod run;
|
||||||
@ -24,15 +22,6 @@ pub mod limits;
|
|||||||
|
|
||||||
pub mod events;
|
pub mod events;
|
||||||
|
|
||||||
pub mod custom_syntax;
|
|
||||||
|
|
||||||
pub mod deprecated;
|
|
||||||
|
|
||||||
pub mod build_type;
|
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
|
||||||
pub mod definitions;
|
|
||||||
|
|
||||||
use crate::{Dynamic, Engine, Identifier};
|
use crate::{Dynamic, Engine, Identifier};
|
||||||
|
|
||||||
#[cfg(feature = "no_std")]
|
#[cfg(feature = "no_std")]
|
||||||
@ -291,3 +280,14 @@ impl Engine {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub mod type_names;
|
||||||
|
|
||||||
|
pub mod custom_syntax;
|
||||||
|
|
||||||
|
pub mod build_type;
|
||||||
|
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
pub mod definitions;
|
||||||
|
|
||||||
|
pub mod deprecated;
|
||||||
|
@ -51,20 +51,14 @@ impl Engine {
|
|||||||
) -> AST {
|
) -> AST {
|
||||||
let mut ast = ast;
|
let mut ast = ast;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
let mut _new_ast = self.optimize_into_ast(
|
||||||
let functions = ast
|
|
||||||
.shared_lib()
|
|
||||||
.iter_fn()
|
|
||||||
.filter(|f| f.func.is_script())
|
|
||||||
.map(|f| f.func.get_script_fn_def().unwrap().clone())
|
|
||||||
.collect();
|
|
||||||
|
|
||||||
let mut _new_ast = crate::optimizer::optimize_into_ast(
|
|
||||||
self,
|
|
||||||
scope,
|
scope,
|
||||||
ast.take_statements(),
|
ast.take_statements(),
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
functions,
|
ast.shared_lib()
|
||||||
|
.iter_fn()
|
||||||
|
.map(|f| f.func.get_script_fn_def().expect("`ScriptFnDef").clone())
|
||||||
|
.collect(),
|
||||||
optimization_level,
|
optimization_level,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -58,14 +58,10 @@ impl Engine {
|
|||||||
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
|
pub fn run_with_scope(&self, scope: &mut Scope, script: &str) -> RhaiResultOf<()> {
|
||||||
let scripts = [script];
|
let scripts = [script];
|
||||||
let ast = {
|
let ast = {
|
||||||
|
let (stream, tc) = self.lex_raw(&scripts, self.token_mapper.as_deref());
|
||||||
let interned_strings = &mut *locked_write(&self.interned_strings);
|
let interned_strings = &mut *locked_write(&self.interned_strings);
|
||||||
|
let state = &mut ParseState::new(scope, interned_strings, tc);
|
||||||
let (stream, tokenizer_control) =
|
self.parse(stream.peekable(), state, self.optimization_level)?
|
||||||
self.lex_raw(&scripts, self.token_mapper.as_ref().map(<_>::as_ref));
|
|
||||||
|
|
||||||
let mut state = ParseState::new(scope, interned_strings, tokenizer_control);
|
|
||||||
|
|
||||||
self.parse(&mut stream.peekable(), &mut state, self.optimization_level)?
|
|
||||||
};
|
};
|
||||||
self.run_ast_with_scope(scope, &ast)
|
self.run_ast_with_scope(scope, &ast)
|
||||||
}
|
}
|
||||||
@ -136,7 +132,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
global.debugger_mut().status = crate::eval::DebuggerStatus::Terminate;
|
||||||
let mut this = crate::Dynamic::NULL;
|
let mut this = crate::Dynamic::NULL;
|
||||||
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
let node = &crate::ast::Stmt::Noop(crate::Position::NONE);
|
||||||
|
@ -208,7 +208,8 @@ pub struct FnCallExpr {
|
|||||||
/// Does this function call capture the parent scope?
|
/// Does this function call capture the parent scope?
|
||||||
pub capture_parent_scope: bool,
|
pub capture_parent_scope: bool,
|
||||||
/// Is this function call a native operator?
|
/// Is this function call a native operator?
|
||||||
pub op_token: Option<Token>,
|
/// Otherwise set to [`Token::NonToken`].
|
||||||
|
pub op_token: Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for FnCallExpr {
|
impl fmt::Debug for FnCallExpr {
|
||||||
@ -223,8 +224,8 @@ impl fmt::Debug for FnCallExpr {
|
|||||||
ff.field("hash", &self.hashes)
|
ff.field("hash", &self.hashes)
|
||||||
.field("name", &self.name)
|
.field("name", &self.name)
|
||||||
.field("args", &self.args);
|
.field("args", &self.args);
|
||||||
if let Some(ref token) = self.op_token {
|
if self.op_token != Token::NonToken {
|
||||||
ff.field("op_token", token);
|
ff.field("op_token", &self.op_token);
|
||||||
}
|
}
|
||||||
if self.capture_parent_scope {
|
if self.capture_parent_scope {
|
||||||
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
ff.field("capture_parent_scope", &self.capture_parent_scope);
|
||||||
@ -589,7 +590,7 @@ impl Expr {
|
|||||||
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
hashes: calc_fn_hash(None, f.fn_name(), 1).into(),
|
||||||
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
args: once(Self::StringConstant(f.fn_name().into(), pos)).collect(),
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
op_token: None,
|
op_token: Token::NonToken,
|
||||||
}
|
}
|
||||||
.into(),
|
.into(),
|
||||||
pos,
|
pos,
|
||||||
|
@ -24,7 +24,7 @@ pub struct EncapsulatedEnviron {
|
|||||||
/// Imported [modules][crate::Module].
|
/// Imported [modules][crate::Module].
|
||||||
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
pub imports: Box<[(ImmutableString, crate::SharedModule)]>,
|
||||||
/// Globally-defined constants.
|
/// Globally-defined constants.
|
||||||
pub constants: Option<crate::eval::GlobalConstants>,
|
pub constants: Option<crate::eval::SharedGlobalConstants>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// _(internals)_ A type containing information on a script-defined function.
|
/// _(internals)_ A type containing information on a script-defined function.
|
||||||
|
@ -61,10 +61,8 @@ impl OpAssignment {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
|
pub fn new_op_assignment(name: &str, pos: Position) -> Self {
|
||||||
Self::new_op_assignment_from_token(
|
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
Self::new_op_assignment_from_token(op, pos)
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/// Create a new [`OpAssignment`] from a [`Token`].
|
/// Create a new [`OpAssignment`] from a [`Token`].
|
||||||
///
|
///
|
||||||
@ -72,10 +70,11 @@ impl OpAssignment {
|
|||||||
///
|
///
|
||||||
/// Panics if the token is not an op-assignment operator.
|
/// Panics if the token is not an op-assignment operator.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_op_assignment_from_token(op: &Token, pos: Position) -> Self {
|
pub fn new_op_assignment_from_token(op: Token, pos: Position) -> Self {
|
||||||
let op_raw = op
|
let op_raw = op
|
||||||
.get_base_op_from_assignment()
|
.get_base_op_from_assignment()
|
||||||
.expect("op-assignment operator");
|
.expect("op-assignment operator");
|
||||||
|
|
||||||
Self {
|
Self {
|
||||||
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
|
hash_op_assign: calc_fn_hash(None, op.literal_syntax(), 2),
|
||||||
hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
|
hash_op: calc_fn_hash(None, op_raw.literal_syntax(), 2),
|
||||||
@ -92,10 +91,8 @@ impl OpAssignment {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
pub fn new_op_assignment_from_base(name: &str, pos: Position) -> Self {
|
||||||
Self::new_op_assignment_from_base_token(
|
let op = Token::lookup_symbol_from_syntax(name).expect("operator");
|
||||||
&Token::lookup_symbol_from_syntax(name).expect("operator"),
|
Self::new_op_assignment_from_base_token(op, pos)
|
||||||
pos,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
/// Convert a [`Token`] into a new [`OpAssignment`].
|
/// Convert a [`Token`] into a new [`OpAssignment`].
|
||||||
///
|
///
|
||||||
@ -104,8 +101,8 @@ impl OpAssignment {
|
|||||||
/// Panics if the token is cannot be converted into an op-assignment operator.
|
/// Panics if the token is cannot be converted into an op-assignment operator.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn new_op_assignment_from_base_token(op: &Token, pos: Position) -> Self {
|
pub fn new_op_assignment_from_base_token(op: Token, pos: Position) -> Self {
|
||||||
Self::new_op_assignment_from_token(&op.convert_to_op_assignment().expect("operator"), pos)
|
Self::new_op_assignment_from_token(op.convert_to_op_assignment().expect("operator"), pos)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,7 +147,7 @@ pub struct Engine {
|
|||||||
|
|
||||||
/// Callback closure for debugging.
|
/// Callback closure for debugging.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub(crate) debugger: Option<
|
pub(crate) debugger_interface: Option<
|
||||||
Box<(
|
Box<(
|
||||||
Box<crate::eval::OnDebuggingInit>,
|
Box<crate::eval::OnDebuggingInit>,
|
||||||
Box<crate::eval::OnDebuggerCallback>,
|
Box<crate::eval::OnDebuggerCallback>,
|
||||||
@ -305,7 +305,7 @@ impl Engine {
|
|||||||
limits: crate::api::limits::Limits::new(),
|
limits: crate::api::limits::Limits::new(),
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
debugger: None,
|
debugger_interface: None,
|
||||||
};
|
};
|
||||||
|
|
||||||
// Add the global namespace module
|
// Add the global namespace module
|
||||||
@ -348,4 +348,12 @@ impl Engine {
|
|||||||
pub fn const_empty_string(&self) -> ImmutableString {
|
pub fn const_empty_string(&self) -> ImmutableString {
|
||||||
self.get_interned_string("")
|
self.get_interned_string("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Is there a debugger interface registered with this [`Engine`]?
|
||||||
|
#[cfg(feature = "debugging")]
|
||||||
|
#[inline(always)]
|
||||||
|
#[must_use]
|
||||||
|
pub(crate) const fn is_debugger_registered(&self) -> bool {
|
||||||
|
self.debugger_interface.is_some()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -5,8 +5,8 @@ use super::{Caches, GlobalRuntimeState, Target};
|
|||||||
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
use crate::ast::{ASTFlags, Expr, OpAssignment};
|
||||||
use crate::config::hashing::SusLock;
|
use crate::config::hashing::SusLock;
|
||||||
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
use crate::engine::{FN_IDX_GET, FN_IDX_SET};
|
||||||
use crate::types::dynamic::Union;
|
use crate::tokenizer::Token;
|
||||||
use crate::types::RestoreOnDrop;
|
use crate::types::{dynamic::Union, RestoreOnDrop};
|
||||||
use crate::{
|
use crate::{
|
||||||
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
calc_fn_hash, Dynamic, Engine, FnArgsVec, Position, RhaiResult, RhaiResultOf, Scope, ERR,
|
||||||
};
|
};
|
||||||
@ -76,7 +76,7 @@ impl Engine {
|
|||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
FN_IDX_GET,
|
FN_IDX_GET,
|
||||||
None,
|
Token::NonToken,
|
||||||
hash_idx().0,
|
hash_idx().0,
|
||||||
&mut [target, idx],
|
&mut [target, idx],
|
||||||
true,
|
true,
|
||||||
@ -105,7 +105,7 @@ impl Engine {
|
|||||||
global,
|
global,
|
||||||
caches,
|
caches,
|
||||||
FN_IDX_SET,
|
FN_IDX_SET,
|
||||||
None,
|
Token::NonToken,
|
||||||
hash_idx().1,
|
hash_idx().1,
|
||||||
&mut [target, idx, new_val],
|
&mut [target, idx, new_val],
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -766,7 +766,14 @@ impl Engine {
|
|||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
let (mut orig_val, ..) = self
|
let (mut orig_val, ..) = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, getter, None, *hash_get, args, is_ref_mut, *pos,
|
global,
|
||||||
|
caches,
|
||||||
|
getter,
|
||||||
|
Token::NonToken,
|
||||||
|
*hash_get,
|
||||||
|
args,
|
||||||
|
is_ref_mut,
|
||||||
|
*pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -799,7 +806,14 @@ impl Engine {
|
|||||||
|
|
||||||
let args = &mut [target.as_mut(), &mut new_val];
|
let args = &mut [target.as_mut(), &mut new_val];
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global, caches, setter, None, *hash_set, args, is_ref_mut, *pos,
|
global,
|
||||||
|
caches,
|
||||||
|
setter,
|
||||||
|
Token::NonToken,
|
||||||
|
*hash_set,
|
||||||
|
args,
|
||||||
|
is_ref_mut,
|
||||||
|
*pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
// Try an indexer if property does not exist
|
// Try an indexer if property does not exist
|
||||||
@ -825,7 +839,14 @@ impl Engine {
|
|||||||
let ((getter, hash_get), _, name) = &**x;
|
let ((getter, hash_get), _, name) = &**x;
|
||||||
let args = &mut [target.as_mut()];
|
let args = &mut [target.as_mut()];
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global, caches, getter, None, *hash_get, args, is_ref_mut, *pos,
|
global,
|
||||||
|
caches,
|
||||||
|
getter,
|
||||||
|
Token::NonToken,
|
||||||
|
*hash_get,
|
||||||
|
args,
|
||||||
|
is_ref_mut,
|
||||||
|
*pos,
|
||||||
)
|
)
|
||||||
.map_or_else(
|
.map_or_else(
|
||||||
|err| match *err {
|
|err| match *err {
|
||||||
@ -921,7 +942,13 @@ impl Engine {
|
|||||||
// Assume getters are always pure
|
// Assume getters are always pure
|
||||||
let (mut val, ..) = self
|
let (mut val, ..) = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
global, caches, getter, None, *hash_get, args, is_ref_mut,
|
global,
|
||||||
|
caches,
|
||||||
|
getter,
|
||||||
|
Token::NonToken,
|
||||||
|
*hash_get,
|
||||||
|
args,
|
||||||
|
is_ref_mut,
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.or_else(|err| match *err {
|
.or_else(|err| match *err {
|
||||||
@ -955,7 +982,13 @@ impl Engine {
|
|||||||
let mut arg_values = [target.as_mut(), val.as_mut()];
|
let mut arg_values = [target.as_mut(), val.as_mut()];
|
||||||
let args = &mut arg_values;
|
let args = &mut arg_values;
|
||||||
self.exec_native_fn_call(
|
self.exec_native_fn_call(
|
||||||
global, caches, setter, None, *hash_set, args, is_ref_mut,
|
global,
|
||||||
|
caches,
|
||||||
|
setter,
|
||||||
|
Token::NonToken,
|
||||||
|
*hash_set,
|
||||||
|
args,
|
||||||
|
is_ref_mut,
|
||||||
pos,
|
pos,
|
||||||
)
|
)
|
||||||
.or_else(
|
.or_else(
|
||||||
|
@ -415,7 +415,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Dynamic,
|
this_ptr: &mut Dynamic,
|
||||||
node: impl Into<ASTNode<'a>>,
|
node: impl Into<ASTNode<'a>>,
|
||||||
) -> RhaiResultOf<()> {
|
) -> RhaiResultOf<()> {
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
if let Some(cmd) =
|
if let Some(cmd) =
|
||||||
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
|
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)?
|
||||||
{
|
{
|
||||||
@ -440,7 +440,7 @@ impl Engine {
|
|||||||
this_ptr: &mut Dynamic,
|
this_ptr: &mut Dynamic,
|
||||||
node: impl Into<ASTNode<'a>>,
|
node: impl Into<ASTNode<'a>>,
|
||||||
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
) -> RhaiResultOf<Option<DebuggerStatus>> {
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)
|
self.run_debugger_with_reset_raw(global, caches, scope, this_ptr, node)
|
||||||
} else {
|
} else {
|
||||||
Ok(None)
|
Ok(None)
|
||||||
@ -512,7 +512,7 @@ impl Engine {
|
|||||||
let src = src.as_ref().map(|s| s.as_str());
|
let src = src.as_ref().map(|s| s.as_str());
|
||||||
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
|
let context = crate::EvalContext::new(self, global, caches, scope, this_ptr);
|
||||||
|
|
||||||
if let Some(ref x) = self.debugger {
|
if let Some(ref x) = self.debugger_interface {
|
||||||
let (.., ref on_debugger) = **x;
|
let (.., ref on_debugger) = **x;
|
||||||
|
|
||||||
let command = on_debugger(context, event, node, src, node.position())?;
|
let command = on_debugger(context, event, node, src, node.position())?;
|
||||||
|
@ -8,7 +8,7 @@ use std::prelude::v1::*;
|
|||||||
/// Collection of globally-defined constants.
|
/// Collection of globally-defined constants.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub type GlobalConstants =
|
pub type SharedGlobalConstants =
|
||||||
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
crate::Shared<crate::Locked<std::collections::BTreeMap<ImmutableString, Dynamic>>>;
|
||||||
|
|
||||||
/// _(internals)_ Global runtime states.
|
/// _(internals)_ Global runtime states.
|
||||||
@ -67,12 +67,12 @@ pub struct GlobalRuntimeState {
|
|||||||
/// Interior mutability is needed because it is shared in order to aid in cloning.
|
/// Interior mutability is needed because it is shared in order to aid in cloning.
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub constants: Option<GlobalConstants>,
|
pub constants: Option<SharedGlobalConstants>,
|
||||||
/// Custom state that can be used by the external host.
|
/// Custom state that can be used by the external host.
|
||||||
pub tag: Dynamic,
|
pub tag: Dynamic,
|
||||||
/// Debugging interface.
|
/// Debugging interface.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub(crate) debugger: Option<super::Debugger>,
|
pub(crate) debugger: Option<Box<super::Debugger>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl GlobalRuntimeState {
|
impl GlobalRuntimeState {
|
||||||
@ -103,9 +103,9 @@ impl GlobalRuntimeState {
|
|||||||
tag: engine.default_tag().clone(),
|
tag: engine.default_tag().clone(),
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
debugger: engine.debugger.as_ref().map(|x| {
|
debugger: engine.debugger_interface.as_ref().map(|x| {
|
||||||
let dbg = crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init);
|
let dbg = crate::eval::Debugger::new(crate::eval::DebuggerStatus::Init);
|
||||||
(x.0)(engine, dbg)
|
(x.0)(engine, dbg).into()
|
||||||
}),
|
}),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -116,7 +116,7 @@ impl GlobalRuntimeState {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn num_imports(&self) -> usize {
|
pub fn num_imports(&self) -> usize {
|
||||||
self.modules.as_ref().map_or(0, |m| m.len())
|
self.modules.as_deref().map_or(0, crate::StaticVec::len)
|
||||||
}
|
}
|
||||||
/// Get the globally-imported [module][crate::Module] at a particular index.
|
/// Get the globally-imported [module][crate::Module] at a particular index.
|
||||||
///
|
///
|
||||||
@ -139,7 +139,7 @@ impl GlobalRuntimeState {
|
|||||||
&mut self,
|
&mut self,
|
||||||
index: usize,
|
index: usize,
|
||||||
) -> Option<&mut crate::SharedModule> {
|
) -> Option<&mut crate::SharedModule> {
|
||||||
self.modules.as_mut().and_then(|m| m.get_mut(index))
|
self.modules.as_deref_mut().and_then(|m| m.get_mut(index))
|
||||||
}
|
}
|
||||||
/// Get the index of a globally-imported [module][crate::Module] by name.
|
/// Get the index of a globally-imported [module][crate::Module] by name.
|
||||||
///
|
///
|
||||||
@ -184,8 +184,8 @@ impl GlobalRuntimeState {
|
|||||||
self.imports = None;
|
self.imports = None;
|
||||||
self.modules = None;
|
self.modules = None;
|
||||||
} else if self.imports.is_some() {
|
} else if self.imports.is_some() {
|
||||||
self.imports.as_mut().unwrap().truncate(size);
|
self.imports.as_deref_mut().unwrap().truncate(size);
|
||||||
self.modules.as_mut().unwrap().truncate(size);
|
self.modules.as_deref_mut().unwrap().truncate(size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
/// Get an iterator to the stack of globally-imported [modules][crate::Module] in reverse order.
|
||||||
@ -235,7 +235,7 @@ impl GlobalRuntimeState {
|
|||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
|
pub(crate) fn may_contain_dynamic_fn(&self, hash_script: u64) -> bool {
|
||||||
self.modules.as_ref().map_or(false, |m| {
|
self.modules.as_deref().map_or(false, |m| {
|
||||||
m.iter().any(|m| m.may_contain_dynamic_fn(hash_script))
|
m.iter().any(|m| m.may_contain_dynamic_fn(hash_script))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -324,7 +324,7 @@ impl GlobalRuntimeState {
|
|||||||
/// Panics if the debugging interface is not set.
|
/// Panics if the debugging interface is not set.
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
pub fn debugger_mut(&mut self) -> &mut super::Debugger {
|
pub fn debugger_mut(&mut self) -> &mut super::Debugger {
|
||||||
self.debugger.as_mut().unwrap()
|
self.debugger.as_deref_mut().unwrap()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -17,10 +17,10 @@ pub use debugger::{
|
|||||||
OnDebuggerCallback, OnDebuggingInit,
|
OnDebuggerCallback, OnDebuggingInit,
|
||||||
};
|
};
|
||||||
pub use eval_context::EvalContext;
|
pub use eval_context::EvalContext;
|
||||||
|
pub use global_state::GlobalRuntimeState;
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
pub use global_state::GlobalConstants;
|
pub use global_state::SharedGlobalConstants;
|
||||||
pub use global_state::GlobalRuntimeState;
|
|
||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub use target::calc_offset_len;
|
pub use target::calc_offset_len;
|
||||||
pub use target::{calc_index, Target};
|
pub use target::{calc_index, Target};
|
||||||
|
@ -129,7 +129,8 @@ impl Engine {
|
|||||||
let args = &mut [&mut *lock_guard, &mut new_val];
|
let args = &mut [&mut *lock_guard, &mut new_val];
|
||||||
|
|
||||||
if self.fast_operators() {
|
if self.fast_operators() {
|
||||||
if let Some(func) = get_builtin_op_assignment_fn(op_assign_token, args[0], args[1])
|
if let Some(func) =
|
||||||
|
get_builtin_op_assignment_fn(op_assign_token.clone(), args[0], args[1])
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
let op = op_assign_token.literal_syntax();
|
let op = op_assign_token.literal_syntax();
|
||||||
@ -145,7 +146,7 @@ impl Engine {
|
|||||||
|
|
||||||
let op_assign = op_assign_token.literal_syntax();
|
let op_assign = op_assign_token.literal_syntax();
|
||||||
let op = op_token.literal_syntax();
|
let op = op_token.literal_syntax();
|
||||||
let token = Some(op_assign_token);
|
let token = op_assign_token.clone();
|
||||||
|
|
||||||
match self
|
match self
|
||||||
.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos)
|
.exec_native_fn_call(global, caches, op_assign, token, hash, args, true, *op_pos)
|
||||||
@ -154,7 +155,7 @@ impl Engine {
|
|||||||
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
Err(err) if matches!(*err, ERR::ErrorFunctionNotFound(ref f, ..) if f.starts_with(op_assign)) =>
|
||||||
{
|
{
|
||||||
// Expand to `var = var op rhs`
|
// Expand to `var = var op rhs`
|
||||||
let token = Some(op_token);
|
let token = op_token.clone();
|
||||||
|
|
||||||
*args[0] = self
|
*args[0] = self
|
||||||
.exec_native_fn_call(
|
.exec_native_fn_call(
|
||||||
|
@ -84,7 +84,7 @@ fn const_false_fn(_: NativeCallContext, _: &mut [&mut Dynamic]) -> RhaiResult {
|
|||||||
///
|
///
|
||||||
/// The return function will be registered as a _method_, so the first parameter cannot be consumed.
|
/// The return function will be registered as a _method_, so the first parameter cannot be consumed.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
pub fn get_builtin_binary_op_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||||
let type1 = x.type_id();
|
let type1 = x.type_id();
|
||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
@ -587,7 +587,7 @@ pub fn get_builtin_binary_op_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<
|
|||||||
///
|
///
|
||||||
/// The return function is registered as a _method_, so the first parameter cannot be consumed.
|
/// The return function is registered as a _method_, so the first parameter cannot be consumed.
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn get_builtin_op_assignment_fn(op: &Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
pub fn get_builtin_op_assignment_fn(op: Token, x: &Dynamic, y: &Dynamic) -> Option<FnBuiltin> {
|
||||||
let type1 = x.type_id();
|
let type1 = x.type_id();
|
||||||
let type2 = y.type_id();
|
let type2 = y.type_id();
|
||||||
|
|
||||||
|
@ -165,7 +165,7 @@ impl Engine {
|
|||||||
_global: &GlobalRuntimeState,
|
_global: &GlobalRuntimeState,
|
||||||
caches: &'s mut Caches,
|
caches: &'s mut Caches,
|
||||||
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
local_entry: &'s mut Option<FnResolutionCacheEntry>,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
hash_base: u64,
|
hash_base: u64,
|
||||||
args: Option<&mut FnCallArgs>,
|
args: Option<&mut FnCallArgs>,
|
||||||
allow_dynamic: bool,
|
allow_dynamic: bool,
|
||||||
@ -174,7 +174,7 @@ impl Engine {
|
|||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut hash = args.as_ref().map_or(hash_base, |args| {
|
let mut hash = args.as_deref().map_or(hash_base, |args| {
|
||||||
calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
|
calc_fn_hash_full(hash_base, args.iter().map(|a| a.type_id()))
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -183,7 +183,7 @@ impl Engine {
|
|||||||
match cache.map.entry(hash) {
|
match cache.map.entry(hash) {
|
||||||
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
Entry::Occupied(entry) => entry.into_mut().as_ref(),
|
||||||
Entry::Vacant(entry) => {
|
Entry::Vacant(entry) => {
|
||||||
let num_args = args.as_ref().map_or(0, |a| a.len());
|
let num_args = args.as_deref().map_or(0, |a| a.len());
|
||||||
let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
|
let mut max_bitmask = 0; // One above maximum bitmask based on number of parameters.
|
||||||
// Set later when a specific matching function is not found.
|
// Set later when a specific matching function is not found.
|
||||||
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
let mut bitmask = 1usize; // Bitmask of which parameter to replace with `Dynamic`
|
||||||
@ -272,7 +272,8 @@ impl Engine {
|
|||||||
// Try to find a built-in version
|
// Try to find a built-in version
|
||||||
let builtin =
|
let builtin =
|
||||||
args.and_then(|args| match op_token {
|
args.and_then(|args| match op_token {
|
||||||
Some(token) if token.is_op_assignment() => {
|
Token::NonToken => None,
|
||||||
|
token if token.is_op_assignment() => {
|
||||||
let (first_arg, rest_args) = args.split_first().unwrap();
|
let (first_arg, rest_args) = args.split_first().unwrap();
|
||||||
|
|
||||||
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
get_builtin_op_assignment_fn(token, first_arg, rest_args[0])
|
||||||
@ -281,13 +282,11 @@ impl Engine {
|
|||||||
source: None,
|
source: None,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
Some(token) => get_builtin_binary_op_fn(token, args[0], args[1])
|
token => get_builtin_binary_op_fn(token.clone(), args[0], args[1])
|
||||||
.map(|f| FnResolutionCacheEntry {
|
.map(|f| FnResolutionCacheEntry {
|
||||||
func: CallableFunction::Method(Shared::new(f)),
|
func: CallableFunction::Method(Shared::new(f)),
|
||||||
source: None,
|
source: None,
|
||||||
}),
|
}),
|
||||||
|
|
||||||
None => None,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return if cache.filter.is_absent_and_set(hash) {
|
return if cache.filter.is_absent_and_set(hash) {
|
||||||
@ -340,7 +339,7 @@ impl Engine {
|
|||||||
global: &mut GlobalRuntimeState,
|
global: &mut GlobalRuntimeState,
|
||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
name: &str,
|
name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
hash: u64,
|
hash: u64,
|
||||||
args: &mut FnCallArgs,
|
args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -385,7 +384,7 @@ impl Engine {
|
|||||||
&mut *RestoreOnDrop::lock_if(swap, args, move |a| backup.restore_first_arg(a));
|
&mut *RestoreOnDrop::lock_if(swap, args, move |a| backup.restore_first_arg(a));
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let source = source.clone().or_else(|| global.source.clone());
|
let source = source.clone().or_else(|| global.source.clone());
|
||||||
|
|
||||||
global.debugger_mut().push_call_stack_frame(
|
global.debugger_mut().push_call_stack_frame(
|
||||||
@ -417,7 +416,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
use crate::eval::{DebuggerEvent, DebuggerStatus};
|
||||||
|
|
||||||
let trigger = match global.debugger().status {
|
let trigger = match global.debugger().status {
|
||||||
@ -559,7 +558,7 @@ impl Engine {
|
|||||||
caches: &mut Caches,
|
caches: &mut Caches,
|
||||||
_scope: Option<&mut Scope>,
|
_scope: Option<&mut Scope>,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
mut _args: &mut FnCallArgs,
|
mut _args: &mut FnCallArgs,
|
||||||
is_ref_mut: bool,
|
is_ref_mut: bool,
|
||||||
@ -638,7 +637,15 @@ impl Engine {
|
|||||||
let local_entry = &mut None;
|
let local_entry = &mut None;
|
||||||
|
|
||||||
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
if let Some(FnResolutionCacheEntry { func, ref source }) = self
|
||||||
.resolve_fn(global, caches, local_entry, None, hash, None, false)
|
.resolve_fn(
|
||||||
|
global,
|
||||||
|
caches,
|
||||||
|
local_entry,
|
||||||
|
Token::NonToken,
|
||||||
|
hash,
|
||||||
|
None,
|
||||||
|
false,
|
||||||
|
)
|
||||||
.cloned()
|
.cloned()
|
||||||
{
|
{
|
||||||
// Script function call
|
// Script function call
|
||||||
@ -721,7 +728,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Do not match function exit for arguments
|
// Do not match function exit for arguments
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
let reset = global.debugger.as_mut().and_then(|dbg| {
|
let reset = global.debugger.as_deref_mut().and_then(|dbg| {
|
||||||
dbg.clear_status_if(|status| {
|
dbg.clear_status_if(|status| {
|
||||||
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
matches!(status, crate::eval::DebuggerStatus::FunctionExit(..))
|
||||||
})
|
})
|
||||||
@ -782,7 +789,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
None,
|
Token::NonToken,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
false,
|
false,
|
||||||
@ -836,7 +843,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
&fn_name,
|
&fn_name,
|
||||||
None,
|
Token::NonToken,
|
||||||
new_hash,
|
new_hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -935,7 +942,7 @@ impl Engine {
|
|||||||
caches,
|
caches,
|
||||||
None,
|
None,
|
||||||
fn_name,
|
fn_name,
|
||||||
None,
|
Token::NonToken,
|
||||||
hash,
|
hash,
|
||||||
&mut args,
|
&mut args,
|
||||||
is_ref_mut,
|
is_ref_mut,
|
||||||
@ -961,7 +968,7 @@ impl Engine {
|
|||||||
scope: &mut Scope,
|
scope: &mut Scope,
|
||||||
this_ptr: &mut Dynamic,
|
this_ptr: &mut Dynamic,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
first_arg: Option<&Expr>,
|
first_arg: Option<&Expr>,
|
||||||
args_expr: &[Expr],
|
args_expr: &[Expr],
|
||||||
hashes: FnCallHashes,
|
hashes: FnCallHashes,
|
||||||
@ -977,7 +984,7 @@ impl Engine {
|
|||||||
let redirected; // Handle call() - Redirect function call
|
let redirected; // Handle call() - Redirect function call
|
||||||
|
|
||||||
match name {
|
match name {
|
||||||
_ if op_token.is_some() => (),
|
_ if op_token != Token::NonToken => (),
|
||||||
|
|
||||||
// Handle call()
|
// Handle call()
|
||||||
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
KEYWORD_FN_PTR_CALL if total_args >= 1 => {
|
||||||
@ -1488,10 +1495,10 @@ impl Engine {
|
|||||||
..
|
..
|
||||||
} = expr;
|
} = expr;
|
||||||
|
|
||||||
let op_token = op_token.as_ref();
|
let op_token = op_token.clone();
|
||||||
|
|
||||||
// Short-circuit native binary operator call if under Fast Operators mode
|
// Short-circuit native binary operator call if under Fast Operators mode
|
||||||
if op_token.is_some() && self.fast_operators() && args.len() == 2 {
|
if op_token != Token::NonToken && self.fast_operators() && args.len() == 2 {
|
||||||
let mut lhs = self
|
let mut lhs = self
|
||||||
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
.get_arg_value(global, caches, scope, this_ptr, &args[0])?
|
||||||
.0
|
.0
|
||||||
@ -1504,8 +1511,7 @@ impl Engine {
|
|||||||
|
|
||||||
let operands = &mut [&mut lhs, &mut rhs];
|
let operands = &mut [&mut lhs, &mut rhs];
|
||||||
|
|
||||||
if let Some(func) =
|
if let Some(func) = get_builtin_binary_op_fn(op_token.clone(), operands[0], operands[1])
|
||||||
get_builtin_binary_op_fn(op_token.as_ref().unwrap(), operands[0], operands[1])
|
|
||||||
{
|
{
|
||||||
// Built-in found
|
// Built-in found
|
||||||
let orig_level = global.level;
|
let orig_level = global.level;
|
||||||
|
@ -424,8 +424,7 @@ impl<'a> NativeCallContext<'a> {
|
|||||||
let caches = &mut Caches::new();
|
let caches = &mut Caches::new();
|
||||||
|
|
||||||
let fn_name = fn_name.as_ref();
|
let fn_name = fn_name.as_ref();
|
||||||
let op_token = Token::lookup_symbol_from_syntax(fn_name);
|
let op_token = Token::lookup_symbol_from_syntax(fn_name).unwrap_or(Token::NonToken);
|
||||||
let op_token = op_token.as_ref();
|
|
||||||
let args_len = args.len();
|
let args_len = args.len();
|
||||||
|
|
||||||
global.level += 1;
|
global.level += 1;
|
||||||
|
@ -69,7 +69,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_none() && fn_def.body.is_empty() {
|
if self.debugger_interface.is_none() && fn_def.body.is_empty() {
|
||||||
return Ok(Dynamic::UNIT);
|
return Ok(Dynamic::UNIT);
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "debugging"))]
|
#[cfg(not(feature = "debugging"))]
|
||||||
@ -96,7 +96,7 @@ impl Engine {
|
|||||||
|
|
||||||
// Push a new call stack frame
|
// Push a new call stack frame
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let source = global.source.clone();
|
let source = global.source.clone();
|
||||||
|
|
||||||
global.debugger_mut().push_call_stack_frame(
|
global.debugger_mut().push_call_stack_frame(
|
||||||
@ -131,7 +131,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
let node = crate::ast::Stmt::Noop(fn_def.body.position());
|
||||||
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
self.run_debugger(global, caches, scope, this_ptr, &node)?;
|
||||||
}
|
}
|
||||||
@ -161,7 +161,7 @@ impl Engine {
|
|||||||
});
|
});
|
||||||
|
|
||||||
#[cfg(feature = "debugging")]
|
#[cfg(feature = "debugging")]
|
||||||
if self.debugger.is_some() {
|
if self.is_debugger_registered() {
|
||||||
let trigger = match global.debugger_mut().status {
|
let trigger = match global.debugger_mut().status {
|
||||||
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
crate::eval::DebuggerStatus::FunctionExit(n) => n >= global.level,
|
||||||
crate::eval::DebuggerStatus::Next(.., true) => true,
|
crate::eval::DebuggerStatus::Next(.., true) => true,
|
||||||
|
@ -392,7 +392,7 @@ impl Module {
|
|||||||
#[inline]
|
#[inline]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn doc(&self) -> &str {
|
pub fn doc(&self) -> &str {
|
||||||
self.doc.as_ref().map_or("", |s| s.as_str())
|
self.doc.as_deref().map_or("", SmartString::as_str)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Set the documentation of the [`Module`].
|
/// Set the documentation of the [`Module`].
|
||||||
@ -542,16 +542,28 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn is_empty(&self) -> bool {
|
pub fn is_empty(&self) -> bool {
|
||||||
!self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
|
!self.flags.contains(ModuleFlags::INDEXED_GLOBAL_FUNCTIONS)
|
||||||
&& self.functions.as_ref().map_or(true, |m| m.is_empty())
|
&& self
|
||||||
&& self.variables.as_ref().map_or(true, |m| m.is_empty())
|
.functions
|
||||||
&& self.modules.as_ref().map_or(true, |m| m.is_empty())
|
.as_ref()
|
||||||
&& self.type_iterators.as_ref().map_or(true, |t| t.is_empty())
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
&& self.all_functions.as_ref().map_or(true, |m| m.is_empty())
|
&& self.variables.as_deref().map_or(true, BTreeMap::is_empty)
|
||||||
&& self.all_variables.as_ref().map_or(true, |m| m.is_empty())
|
&& self.modules.as_deref().map_or(true, BTreeMap::is_empty)
|
||||||
|
&& self
|
||||||
|
.type_iterators
|
||||||
|
.as_deref()
|
||||||
|
.map_or(true, BTreeMap::is_empty)
|
||||||
|
&& self
|
||||||
|
.all_functions
|
||||||
|
.as_deref()
|
||||||
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
|
&& self
|
||||||
|
.all_variables
|
||||||
|
.as_deref()
|
||||||
|
.map_or(true, StraightHashMap::is_empty)
|
||||||
&& self
|
&& self
|
||||||
.all_type_iterators
|
.all_type_iterators
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(true, |m| m.is_empty())
|
.map_or(true, BTreeMap::is_empty)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is the [`Module`] indexed?
|
/// Is the [`Module`] indexed?
|
||||||
@ -1633,8 +1645,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1689,8 +1701,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1754,8 +1766,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1837,8 +1849,8 @@ impl Module {
|
|||||||
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
self.flags &= !ModuleFlags::INDEXED & !ModuleFlags::INDEXED_GLOBAL_FUNCTIONS;
|
||||||
|
|
||||||
#[cfg(feature = "metadata")]
|
#[cfg(feature = "metadata")]
|
||||||
if !other.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !other.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
if !self.doc.as_ref().map_or(true, |s| s.is_empty()) {
|
if !self.doc.as_deref().map_or(true, SmartString::is_empty) {
|
||||||
self.doc.get_or_insert_with(Default::default).push('\n');
|
self.doc.get_or_insert_with(Default::default).push('\n');
|
||||||
}
|
}
|
||||||
self.doc
|
self.doc
|
||||||
@ -1886,9 +1898,9 @@ impl Module {
|
|||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn count(&self) -> (usize, usize, usize) {
|
pub fn count(&self) -> (usize, usize, usize) {
|
||||||
(
|
(
|
||||||
self.variables.as_ref().map_or(0, |m| m.len()),
|
self.variables.as_deref().map_or(0, BTreeMap::len),
|
||||||
self.functions.as_ref().map_or(0, |m| m.len()),
|
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
||||||
self.type_iterators.as_ref().map_or(0, |t| t.len()),
|
self.type_iterators.as_deref().map_or(0, BTreeMap::len),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2260,11 +2272,11 @@ 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 = StraightHashMap::with_capacity_and_hasher(
|
let mut variables = StraightHashMap::with_capacity_and_hasher(
|
||||||
self.variables.as_ref().map_or(0, |m| m.len()),
|
self.variables.as_deref().map_or(0, BTreeMap::len),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
let mut functions = StraightHashMap::with_capacity_and_hasher(
|
let mut functions = StraightHashMap::with_capacity_and_hasher(
|
||||||
self.functions.as_ref().map_or(0, |m| m.len()),
|
self.functions.as_ref().map_or(0, StraightHashMap::len),
|
||||||
Default::default(),
|
Default::default(),
|
||||||
);
|
);
|
||||||
let mut type_iterators = BTreeMap::new();
|
let mut type_iterators = BTreeMap::new();
|
||||||
|
@ -170,7 +170,7 @@ impl FileModuleResolver {
|
|||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
#[must_use]
|
#[must_use]
|
||||||
pub fn base_path(&self) -> Option<&Path> {
|
pub fn base_path(&self) -> Option<&Path> {
|
||||||
self.base_path.as_ref().map(<_>::as_ref)
|
self.base_path.as_deref()
|
||||||
}
|
}
|
||||||
/// Set the base path for script files.
|
/// Set the base path for script files.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
|
300
src/optimizer.rs
300
src/optimizer.rs
@ -65,7 +65,7 @@ struct OptimizerState<'a> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl<'a> OptimizerState<'a> {
|
impl<'a> OptimizerState<'a> {
|
||||||
/// Create a new State.
|
/// Create a new [`OptimizerState`].
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn new(
|
pub fn new(
|
||||||
engine: &'a Engine,
|
engine: &'a Engine,
|
||||||
@ -138,7 +138,7 @@ impl<'a> OptimizerState<'a> {
|
|||||||
pub fn call_fn_with_constant_arguments(
|
pub fn call_fn_with_constant_arguments(
|
||||||
&mut self,
|
&mut self,
|
||||||
fn_name: &str,
|
fn_name: &str,
|
||||||
op_token: Option<&Token>,
|
op_token: Token,
|
||||||
arg_values: &mut [Dynamic],
|
arg_values: &mut [Dynamic],
|
||||||
) -> Dynamic {
|
) -> Dynamic {
|
||||||
self.engine
|
self.engine
|
||||||
@ -156,39 +156,6 @@ impl<'a> OptimizerState<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Has a system function a Rust-native override?
|
|
||||||
fn has_native_fn_override(
|
|
||||||
engine: &Engine,
|
|
||||||
hash_script: u64,
|
|
||||||
arg_types: impl AsRef<[TypeId]>,
|
|
||||||
) -> bool {
|
|
||||||
let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied());
|
|
||||||
|
|
||||||
// First check the global namespace and packages, but skip modules that are standard because
|
|
||||||
// they should never conflict with system functions.
|
|
||||||
if engine
|
|
||||||
.global_modules
|
|
||||||
.iter()
|
|
||||||
.filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
|
|
||||||
.any(|m| m.contains_fn(hash))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Then check sub-modules
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
if engine
|
|
||||||
.global_sub_modules
|
|
||||||
.iter()
|
|
||||||
.flat_map(|m| m.values())
|
|
||||||
.any(|m| m.contains_qualified_fn(hash))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Optimize a block of [statements][Stmt].
|
/// Optimize a block of [statements][Stmt].
|
||||||
fn optimize_stmt_block(
|
fn optimize_stmt_block(
|
||||||
mut statements: StmtBlockContainer,
|
mut statements: StmtBlockContainer,
|
||||||
@ -1150,8 +1117,8 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Overloaded operators can override built-in.
|
// Overloaded operators can override built-in.
|
||||||
_ if x.args.len() == 2 && x.op_token.is_some() && (state.engine.fast_operators() || !has_native_fn_override(state.engine, x.hashes.native(), &arg_types)) => {
|
_ if x.args.len() == 2 && x.op_token != Token::NonToken && (state.engine.fast_operators() || !state.engine.has_native_fn_override(x.hashes.native(), &arg_types)) => {
|
||||||
if let Some(result) = get_builtin_binary_op_fn(x.op_token.as_ref().unwrap(), &arg_values[0], &arg_values[1])
|
if let Some(result) = get_builtin_binary_op_fn(x.op_token.clone(), &arg_values[0], &arg_values[1])
|
||||||
.and_then(|f| {
|
.and_then(|f| {
|
||||||
let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into();
|
let context = (state.engine, x.name.as_str(),None, &state.global, *pos).into();
|
||||||
let (first, second) = arg_values.split_first_mut().unwrap();
|
let (first, second) = arg_values.split_first_mut().unwrap();
|
||||||
@ -1204,7 +1171,7 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
|
KEYWORD_TYPE_OF if arg_values.len() == 1 => state.engine.map_type_name(arg_values[0].type_name()).into(),
|
||||||
#[cfg(not(feature = "no_closure"))]
|
#[cfg(not(feature = "no_closure"))]
|
||||||
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
|
crate::engine::KEYWORD_IS_SHARED if arg_values.len() == 1 => Dynamic::FALSE,
|
||||||
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.as_ref(), arg_values)
|
_ => state.call_fn_with_constant_arguments(&x.name, x.op_token.clone(), arg_values)
|
||||||
};
|
};
|
||||||
|
|
||||||
if !result.is_null() {
|
if !result.is_null() {
|
||||||
@ -1260,124 +1227,149 @@ fn optimize_expr(expr: &mut Expr, state: &mut OptimizerState, _chaining: bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Optimize a block of [statements][Stmt] at top level.
|
impl Engine {
|
||||||
///
|
/// Has a system function a Rust-native override?
|
||||||
/// Constants and variables from the scope are added.
|
fn has_native_fn_override(&self, hash_script: u64, arg_types: impl AsRef<[TypeId]>) -> bool {
|
||||||
fn optimize_top_level(
|
let hash = calc_fn_hash_full(hash_script, arg_types.as_ref().iter().copied());
|
||||||
statements: StmtBlockContainer,
|
|
||||||
engine: &Engine,
|
// First check the global namespace and packages, but skip modules that are standard because
|
||||||
scope: &Scope,
|
// they should never conflict with system functions.
|
||||||
#[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule],
|
if self
|
||||||
optimization_level: OptimizationLevel,
|
.global_modules
|
||||||
) -> StmtBlockContainer {
|
.iter()
|
||||||
let mut statements = statements;
|
.filter(|m| !m.flags.contains(ModuleFlags::STANDARD_LIB))
|
||||||
|
.any(|m| m.contains_fn(hash))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Then check sub-modules
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
if self
|
||||||
|
.global_sub_modules
|
||||||
|
.iter()
|
||||||
|
.flat_map(|m| m.values())
|
||||||
|
.any(|m| m.contains_qualified_fn(hash))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optimize a block of [statements][Stmt] at top level.
|
||||||
|
///
|
||||||
|
/// Constants and variables from the scope are added.
|
||||||
|
fn optimize_top_level(
|
||||||
|
&self,
|
||||||
|
statements: StmtBlockContainer,
|
||||||
|
scope: &Scope,
|
||||||
|
#[cfg(not(feature = "no_function"))] lib: &[crate::SharedModule],
|
||||||
|
optimization_level: OptimizationLevel,
|
||||||
|
) -> StmtBlockContainer {
|
||||||
|
let mut statements = statements;
|
||||||
|
|
||||||
|
// If optimization level is None then skip optimizing
|
||||||
|
if optimization_level == OptimizationLevel::None {
|
||||||
|
statements.shrink_to_fit();
|
||||||
|
return statements;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set up the state
|
||||||
|
let mut state = OptimizerState::new(
|
||||||
|
self,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
lib,
|
||||||
|
optimization_level,
|
||||||
|
);
|
||||||
|
|
||||||
|
// Add constants from global modules
|
||||||
|
for (name, value) in self.global_modules.iter().rev().flat_map(|m| m.iter_var()) {
|
||||||
|
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add constants and variables from the scope
|
||||||
|
for (name, constant, value) in scope.iter() {
|
||||||
|
if constant {
|
||||||
|
state.push_var(name, AccessMode::ReadOnly, value);
|
||||||
|
} else {
|
||||||
|
state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
optimize_stmt_block(statements, &mut state, true, false, true)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Optimize a collection of statements and functions into an [`AST`].
|
||||||
|
pub(crate) fn optimize_into_ast(
|
||||||
|
&self,
|
||||||
|
scope: &Scope,
|
||||||
|
statements: StmtBlockContainer,
|
||||||
|
#[cfg(not(feature = "no_function"))] functions: StaticVec<
|
||||||
|
crate::Shared<crate::ast::ScriptFnDef>,
|
||||||
|
>,
|
||||||
|
optimization_level: OptimizationLevel,
|
||||||
|
) -> AST {
|
||||||
|
let mut statements = statements;
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
let lib: crate::Shared<_> = {
|
||||||
|
let mut module = crate::Module::new();
|
||||||
|
|
||||||
|
if optimization_level != OptimizationLevel::None {
|
||||||
|
// We only need the script library's signatures for optimization purposes
|
||||||
|
let mut lib2 = crate::Module::new();
|
||||||
|
|
||||||
|
for fn_def in &functions {
|
||||||
|
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
||||||
|
name: fn_def.name.clone(),
|
||||||
|
access: fn_def.access,
|
||||||
|
body: crate::ast::StmtBlock::NONE,
|
||||||
|
params: fn_def.params.clone(),
|
||||||
|
#[cfg(not(feature = "no_module"))]
|
||||||
|
environ: None,
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
#[cfg(feature = "metadata")]
|
||||||
|
comments: Box::default(),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
let lib2 = &[lib2.into()];
|
||||||
|
|
||||||
|
for fn_def in functions {
|
||||||
|
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
||||||
|
|
||||||
|
// Optimize the function body
|
||||||
|
let body = mem::take(&mut *fn_def.body);
|
||||||
|
|
||||||
|
*fn_def.body = self.optimize_top_level(body, scope, lib2, optimization_level);
|
||||||
|
|
||||||
|
module.set_script_fn(fn_def);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for fn_def in functions {
|
||||||
|
module.set_script_fn(fn_def);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
module.into()
|
||||||
|
};
|
||||||
|
|
||||||
// If optimization level is None then skip optimizing
|
|
||||||
if optimization_level == OptimizationLevel::None {
|
|
||||||
statements.shrink_to_fit();
|
statements.shrink_to_fit();
|
||||||
return statements;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set up the state
|
AST::new(
|
||||||
let mut state = OptimizerState::new(
|
match optimization_level {
|
||||||
engine,
|
OptimizationLevel::None => statements,
|
||||||
#[cfg(not(feature = "no_function"))]
|
OptimizationLevel::Simple | OptimizationLevel::Full => self.optimize_top_level(
|
||||||
lib,
|
statements,
|
||||||
optimization_level,
|
scope,
|
||||||
);
|
|
||||||
|
|
||||||
// Add constants from global modules
|
|
||||||
for (name, value) in engine
|
|
||||||
.global_modules
|
|
||||||
.iter()
|
|
||||||
.rev()
|
|
||||||
.flat_map(|m| m.iter_var())
|
|
||||||
{
|
|
||||||
state.push_var(name, AccessMode::ReadOnly, value.clone());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add constants and variables from the scope
|
|
||||||
for (name, constant, value) in scope.iter() {
|
|
||||||
if constant {
|
|
||||||
state.push_var(name, AccessMode::ReadOnly, value);
|
|
||||||
} else {
|
|
||||||
state.push_var(name, AccessMode::ReadWrite, Dynamic::NULL);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
optimize_stmt_block(statements, &mut state, true, false, true)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Optimize an [`AST`].
|
|
||||||
pub fn optimize_into_ast(
|
|
||||||
engine: &Engine,
|
|
||||||
scope: &Scope,
|
|
||||||
statements: StmtBlockContainer,
|
|
||||||
#[cfg(not(feature = "no_function"))] functions: StaticVec<
|
|
||||||
crate::Shared<crate::ast::ScriptFnDef>,
|
|
||||||
>,
|
|
||||||
optimization_level: OptimizationLevel,
|
|
||||||
) -> AST {
|
|
||||||
let mut statements = statements;
|
|
||||||
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
let lib: crate::Shared<_> = {
|
|
||||||
let mut module = crate::Module::new();
|
|
||||||
|
|
||||||
if optimization_level != OptimizationLevel::None {
|
|
||||||
// We only need the script library's signatures for optimization purposes
|
|
||||||
let mut lib2 = crate::Module::new();
|
|
||||||
|
|
||||||
for fn_def in &functions {
|
|
||||||
lib2.set_script_fn(crate::ast::ScriptFnDef {
|
|
||||||
name: fn_def.name.clone(),
|
|
||||||
access: fn_def.access,
|
|
||||||
body: crate::ast::StmtBlock::NONE,
|
|
||||||
params: fn_def.params.clone(),
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
|
||||||
environ: None,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
#[cfg(feature = "metadata")]
|
&[lib.clone()],
|
||||||
comments: Box::default(),
|
optimization_level,
|
||||||
});
|
),
|
||||||
}
|
},
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
let lib2 = &[lib2.into()];
|
lib,
|
||||||
|
)
|
||||||
for fn_def in functions {
|
}
|
||||||
let mut fn_def = crate::func::shared_take_or_clone(fn_def);
|
|
||||||
|
|
||||||
// Optimize the function body
|
|
||||||
let body = mem::take(&mut *fn_def.body);
|
|
||||||
|
|
||||||
*fn_def.body = optimize_top_level(body, engine, scope, lib2, optimization_level);
|
|
||||||
|
|
||||||
module.set_script_fn(fn_def);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for fn_def in functions {
|
|
||||||
module.set_script_fn(fn_def);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
module.into()
|
|
||||||
};
|
|
||||||
|
|
||||||
statements.shrink_to_fit();
|
|
||||||
|
|
||||||
AST::new(
|
|
||||||
match optimization_level {
|
|
||||||
OptimizationLevel::None => statements,
|
|
||||||
OptimizationLevel::Simple | OptimizationLevel::Full => optimize_top_level(
|
|
||||||
statements,
|
|
||||||
engine,
|
|
||||||
scope,
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
&[lib.clone()],
|
|
||||||
optimization_level,
|
|
||||||
),
|
|
||||||
},
|
|
||||||
#[cfg(not(feature = "no_function"))]
|
|
||||||
lib,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
@ -607,7 +607,7 @@ impl Engine {
|
|||||||
.any(|m| m.as_str() == root)
|
.any(|m| m.as_str() == root)
|
||||||
&& !self
|
&& !self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains_key(root))
|
.map_or(false, |m| m.contains_key(root))
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -633,7 +633,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
op_token: None,
|
op_token: Token::NonToken,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -682,7 +682,7 @@ impl Engine {
|
|||||||
.any(|m| m.as_str() == root)
|
.any(|m| m.as_str() == root)
|
||||||
&& !self
|
&& !self
|
||||||
.global_sub_modules
|
.global_sub_modules
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains_key(root))
|
.map_or(false, |m| m.contains_key(root))
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -708,7 +708,7 @@ impl Engine {
|
|||||||
return Ok(FnCallExpr {
|
return Ok(FnCallExpr {
|
||||||
name: state.get_interned_string(id),
|
name: state.get_interned_string(id),
|
||||||
capture_parent_scope,
|
capture_parent_scope,
|
||||||
op_token: None,
|
op_token: Token::NonToken,
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
namespace,
|
namespace,
|
||||||
hashes,
|
hashes,
|
||||||
@ -1413,8 +1413,11 @@ impl Engine {
|
|||||||
Token::Pipe | Token::Or if settings.has_option(LangOptions::ANON_FN) => {
|
Token::Pipe | Token::Or if settings.has_option(LangOptions::ANON_FN) => {
|
||||||
// Build new parse state
|
// Build new parse state
|
||||||
let new_interner = &mut StringsInterner::new();
|
let new_interner = &mut StringsInterner::new();
|
||||||
let mut new_state =
|
let new_state = &mut ParseState::new(
|
||||||
ParseState::new(state.scope, new_interner, state.tokenizer_control.clone());
|
state.scope,
|
||||||
|
new_interner,
|
||||||
|
state.tokenizer_control.clone(),
|
||||||
|
);
|
||||||
|
|
||||||
// We move the strings interner to the new parse state object by swapping it...
|
// We move the strings interner to the new parse state object by swapping it...
|
||||||
std::mem::swap(state.interned_strings, new_state.interned_strings);
|
std::mem::swap(state.interned_strings, new_state.interned_strings);
|
||||||
@ -1455,7 +1458,7 @@ impl Engine {
|
|||||||
..settings
|
..settings
|
||||||
};
|
};
|
||||||
|
|
||||||
let result = self.parse_anon_fn(input, &mut new_state, state, lib, new_settings);
|
let result = self.parse_anon_fn(input, new_state, state, lib, new_settings);
|
||||||
|
|
||||||
// Restore the strings interner by swapping it back
|
// Restore the strings interner by swapping it back
|
||||||
std::mem::swap(state.interned_strings, new_state.interned_strings);
|
std::mem::swap(state.interned_strings, new_state.interned_strings);
|
||||||
@ -1568,12 +1571,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_ref()
|
.as_deref()
|
||||||
.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_ref()
|
.as_deref()
|
||||||
.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);
|
||||||
@ -1885,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_ref()
|
.as_deref()
|
||||||
.map_or(false, |m| m.contains_key(root))
|
.map_or(false, |m| m.contains_key(root))
|
||||||
{
|
{
|
||||||
return Err(
|
return Err(
|
||||||
@ -1954,7 +1957,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string("-"),
|
name: state.get_interned_string("-"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "-", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(token),
|
op_token: token,
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -1983,7 +1986,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string("+"),
|
name: state.get_interned_string("+"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "+", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(token),
|
op_token: token,
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -2005,7 +2008,7 @@ impl Engine {
|
|||||||
name: state.get_interned_string("!"),
|
name: state.get_interned_string("!"),
|
||||||
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
hashes: FnCallHashes::from_native(calc_fn_hash(None, "!", 1)),
|
||||||
args,
|
args,
|
||||||
op_token: Some(token),
|
op_token: token,
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos))
|
.into_fn_call_expr(pos))
|
||||||
@ -2019,7 +2022,7 @@ impl Engine {
|
|||||||
|
|
||||||
/// Make an assignment statement.
|
/// Make an assignment statement.
|
||||||
fn make_assignment_stmt(
|
fn make_assignment_stmt(
|
||||||
op: Option<Token>,
|
op: Token,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
lhs: Expr,
|
lhs: Expr,
|
||||||
rhs: Expr,
|
rhs: Expr,
|
||||||
@ -2052,7 +2055,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let op_info = if let Some(ref op) = op {
|
let op_info = if op != Token::NonToken {
|
||||||
OpAssignment::new_op_assignment_from_token(op, op_pos)
|
OpAssignment::new_op_assignment_from_token(op, op_pos)
|
||||||
} else {
|
} else {
|
||||||
OpAssignment::new_assignment(op_pos)
|
OpAssignment::new_assignment(op_pos)
|
||||||
@ -2133,12 +2136,11 @@ impl Engine {
|
|||||||
) -> ParseResult<Stmt> {
|
) -> ParseResult<Stmt> {
|
||||||
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
let (op, pos) = match input.peek().expect(NEVER_ENDS) {
|
||||||
// var = ...
|
// var = ...
|
||||||
(Token::Equals, ..) => (None, eat_token(input, Token::Equals)),
|
(Token::Equals, ..) => (Token::NonToken, eat_token(input, Token::Equals)),
|
||||||
// var op= ...
|
// var op= ...
|
||||||
(token, ..) if token.is_op_assignment() => input
|
(token, ..) if token.is_op_assignment() => {
|
||||||
.next()
|
input.next().map(|(op, pos)| (op, pos)).expect(NEVER_ENDS)
|
||||||
.map(|(op, pos)| (Some(op), pos))
|
}
|
||||||
.expect(NEVER_ENDS),
|
|
||||||
// Not op-assignment
|
// Not op-assignment
|
||||||
_ => return Ok(Stmt::Expr(lhs.into())),
|
_ => return Ok(Stmt::Expr(lhs.into())),
|
||||||
};
|
};
|
||||||
@ -2327,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_ref()
|
.as_deref()
|
||||||
.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))?,
|
||||||
@ -2353,7 +2355,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_ref()
|
.as_deref()
|
||||||
.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))?,
|
||||||
@ -2380,9 +2382,9 @@ impl Engine {
|
|||||||
let hash = calc_fn_hash(None, &op, 2);
|
let hash = calc_fn_hash(None, &op, 2);
|
||||||
let is_valid_script_function = is_valid_function_name(&op);
|
let is_valid_script_function = is_valid_function_name(&op);
|
||||||
let operator_token = if is_valid_script_function {
|
let operator_token = if is_valid_script_function {
|
||||||
None
|
Token::NonToken
|
||||||
} else {
|
} else {
|
||||||
Some(op_token.clone())
|
op_token.clone()
|
||||||
};
|
};
|
||||||
|
|
||||||
let mut args = StaticVec::new_const();
|
let mut args = StaticVec::new_const();
|
||||||
@ -2446,7 +2448,7 @@ impl Engine {
|
|||||||
Token::Custom(s)
|
Token::Custom(s)
|
||||||
if self
|
if self
|
||||||
.custom_keywords
|
.custom_keywords
|
||||||
.as_ref()
|
.as_deref()
|
||||||
.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) =>
|
||||||
{
|
{
|
||||||
@ -2875,7 +2877,7 @@ impl Engine {
|
|||||||
settings.flags |= ParseSettingFlags::BREAKABLE;
|
settings.flags |= ParseSettingFlags::BREAKABLE;
|
||||||
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
let body = self.parse_block(input, state, lib, settings.level_up()?)?;
|
||||||
|
|
||||||
state.stack.as_mut().unwrap().rewind(prev_stack_len);
|
state.stack.as_deref_mut().unwrap().rewind(prev_stack_len);
|
||||||
|
|
||||||
Ok(Stmt::For(
|
Ok(Stmt::For(
|
||||||
Box::new((loop_var, counter_var, expr, body.into())),
|
Box::new((loop_var, counter_var, expr, body.into())),
|
||||||
@ -2956,7 +2958,7 @@ impl Engine {
|
|||||||
|
|
||||||
let (existing, hit_barrier) = state.find_var(&name);
|
let (existing, hit_barrier) = state.find_var(&name);
|
||||||
|
|
||||||
let stack = state.stack.as_mut().unwrap();
|
let stack = state.stack.as_deref_mut().unwrap();
|
||||||
|
|
||||||
let existing = if !hit_barrier && existing > 0 {
|
let existing = if !hit_barrier && existing > 0 {
|
||||||
let offset = stack.len() - existing;
|
let offset = stack.len() - existing;
|
||||||
@ -3072,7 +3074,7 @@ impl Engine {
|
|||||||
pos: id_pos,
|
pos: id_pos,
|
||||||
},
|
},
|
||||||
Ident {
|
Ident {
|
||||||
name: state.get_interned_string(alias.as_ref().map_or("", <_>::as_ref)),
|
name: state.get_interned_string(alias.as_deref().unwrap_or("")),
|
||||||
pos: alias_pos,
|
pos: alias_pos,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
@ -3121,10 +3123,10 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
|
|
||||||
let prev_entry_stack_len = state.block_stack_len;
|
let prev_entry_stack_len = state.block_stack_len;
|
||||||
state.block_stack_len = state.stack.as_ref().map_or(0, |s| s.len());
|
state.block_stack_len = state.stack.as_deref().map_or(0, Scope::len);
|
||||||
|
|
||||||
#[cfg(not(feature = "no_module"))]
|
#[cfg(not(feature = "no_module"))]
|
||||||
let orig_imports_len = state.imports.as_ref().map_or(0, |m| m.len());
|
let orig_imports_len = state.imports.as_deref().map_or(0, StaticVec::len);
|
||||||
|
|
||||||
let end_pos = loop {
|
let end_pos = loop {
|
||||||
// Terminated?
|
// Terminated?
|
||||||
@ -3293,7 +3295,7 @@ impl Engine {
|
|||||||
match input.next().expect(NEVER_ENDS) {
|
match input.next().expect(NEVER_ENDS) {
|
||||||
(Token::Fn, pos) => {
|
(Token::Fn, pos) => {
|
||||||
// Build new parse state
|
// Build new parse state
|
||||||
let mut new_state = ParseState::new(
|
let new_state = &mut ParseState::new(
|
||||||
state.scope,
|
state.scope,
|
||||||
state.interned_strings,
|
state.interned_strings,
|
||||||
state.tokenizer_control.clone(),
|
state.tokenizer_control.clone(),
|
||||||
@ -3330,7 +3332,7 @@ impl Engine {
|
|||||||
|
|
||||||
let func = self.parse_fn(
|
let func = self.parse_fn(
|
||||||
input,
|
input,
|
||||||
&mut new_state,
|
new_state,
|
||||||
lib,
|
lib,
|
||||||
access,
|
access,
|
||||||
new_settings,
|
new_settings,
|
||||||
@ -3521,7 +3523,7 @@ impl Engine {
|
|||||||
|
|
||||||
if !catch_var.is_empty() {
|
if !catch_var.is_empty() {
|
||||||
// Remove the error variable from the stack
|
// Remove the error variable from the stack
|
||||||
state.stack.as_mut().unwrap().pop();
|
state.stack.as_deref_mut().unwrap().pop();
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Stmt::TryCatch(
|
Ok(Stmt::TryCatch(
|
||||||
@ -3684,7 +3686,7 @@ impl Engine {
|
|||||||
num_externals + 1,
|
num_externals + 1,
|
||||||
)),
|
)),
|
||||||
args,
|
args,
|
||||||
op_token: None,
|
op_token: Token::NonToken,
|
||||||
capture_parent_scope: false,
|
capture_parent_scope: false,
|
||||||
}
|
}
|
||||||
.into_fn_call_expr(pos);
|
.into_fn_call_expr(pos);
|
||||||
@ -3820,7 +3822,7 @@ impl Engine {
|
|||||||
/// Parse a global level expression.
|
/// Parse a global level expression.
|
||||||
pub(crate) fn parse_global_expr(
|
pub(crate) fn parse_global_expr(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
mut input: TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
process_settings: impl FnOnce(&mut ParseSettings),
|
process_settings: impl FnOnce(&mut ParseSettings),
|
||||||
_optimization_level: OptimizationLevel,
|
_optimization_level: OptimizationLevel,
|
||||||
@ -3842,7 +3844,7 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
process_settings(&mut settings);
|
process_settings(&mut settings);
|
||||||
|
|
||||||
let expr = self.parse_expr(input, state, &mut functions, settings)?;
|
let expr = self.parse_expr(&mut input, state, &mut functions, settings)?;
|
||||||
|
|
||||||
assert!(functions.is_empty());
|
assert!(functions.is_empty());
|
||||||
|
|
||||||
@ -3856,8 +3858,7 @@ impl Engine {
|
|||||||
statements.push(Stmt::Expr(expr.into()));
|
statements.push(Stmt::Expr(expr.into()));
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
return Ok(crate::optimizer::optimize_into_ast(
|
return Ok(self.optimize_into_ast(
|
||||||
self,
|
|
||||||
state.scope,
|
state.scope,
|
||||||
statements,
|
statements,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
@ -3876,7 +3877,7 @@ impl Engine {
|
|||||||
/// Parse the global level statements.
|
/// Parse the global level statements.
|
||||||
fn parse_global_level(
|
fn parse_global_level(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
mut input: TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
process_settings: impl FnOnce(&mut ParseSettings),
|
process_settings: impl FnOnce(&mut ParseSettings),
|
||||||
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
) -> ParseResult<(StmtBlockContainer, StaticVec<Shared<ScriptFnDef>>)> {
|
||||||
@ -3893,8 +3894,8 @@ impl Engine {
|
|||||||
};
|
};
|
||||||
process_settings(&mut settings);
|
process_settings(&mut settings);
|
||||||
|
|
||||||
while !input.peek().expect(NEVER_ENDS).0.is_eof() {
|
while input.peek().expect(NEVER_ENDS).0 != Token::EOF {
|
||||||
let stmt = self.parse_stmt(input, state, &mut functions, settings)?;
|
let stmt = self.parse_stmt(&mut input, state, &mut functions, settings)?;
|
||||||
|
|
||||||
if stmt.is_noop() {
|
if stmt.is_noop() {
|
||||||
continue;
|
continue;
|
||||||
@ -3909,7 +3910,7 @@ impl Engine {
|
|||||||
(Token::EOF, ..) => break,
|
(Token::EOF, ..) => break,
|
||||||
// stmt ;
|
// stmt ;
|
||||||
(Token::SemiColon, ..) if need_semicolon => {
|
(Token::SemiColon, ..) if need_semicolon => {
|
||||||
eat_token(input, Token::SemiColon);
|
eat_token(&mut input, Token::SemiColon);
|
||||||
}
|
}
|
||||||
// stmt ;
|
// stmt ;
|
||||||
(Token::SemiColon, ..) if !need_semicolon => (),
|
(Token::SemiColon, ..) if !need_semicolon => (),
|
||||||
@ -3936,15 +3937,14 @@ impl Engine {
|
|||||||
#[inline]
|
#[inline]
|
||||||
pub(crate) fn parse(
|
pub(crate) fn parse(
|
||||||
&self,
|
&self,
|
||||||
input: &mut TokenStream,
|
input: TokenStream,
|
||||||
state: &mut ParseState,
|
state: &mut ParseState,
|
||||||
_optimization_level: OptimizationLevel,
|
_optimization_level: OptimizationLevel,
|
||||||
) -> ParseResult<AST> {
|
) -> ParseResult<AST> {
|
||||||
let (statements, _lib) = self.parse_global_level(input, state, |_| {})?;
|
let (statements, _lib) = self.parse_global_level(input, state, |_| {})?;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_optimize"))]
|
#[cfg(not(feature = "no_optimize"))]
|
||||||
return Ok(crate::optimizer::optimize_into_ast(
|
return Ok(self.optimize_into_ast(
|
||||||
self,
|
|
||||||
state.scope,
|
state.scope,
|
||||||
statements,
|
statements,
|
||||||
#[cfg(not(feature = "no_function"))]
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
@ -584,7 +584,10 @@ pub enum Token {
|
|||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Custom(Box<SmartString>),
|
Custom(Box<SmartString>),
|
||||||
/// End of the input stream.
|
/// End of the input stream.
|
||||||
|
/// Used as a placeholder for the end of input.
|
||||||
EOF,
|
EOF,
|
||||||
|
/// Placeholder to indicate the lack of a token.
|
||||||
|
NonToken,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Token {
|
impl fmt::Display for Token {
|
||||||
@ -610,6 +613,7 @@ impl fmt::Display for Token {
|
|||||||
Comment(s) => f.write_str(s),
|
Comment(s) => f.write_str(s),
|
||||||
|
|
||||||
EOF => f.write_str("{EOF}"),
|
EOF => f.write_str("{EOF}"),
|
||||||
|
NonToken => f.write_str("{NONE}"),
|
||||||
|
|
||||||
token => f.write_str(token.literal_syntax()),
|
token => f.write_str(token.literal_syntax()),
|
||||||
}
|
}
|
||||||
@ -638,7 +642,7 @@ impl Token {
|
|||||||
Custom(..) => false,
|
Custom(..) => false,
|
||||||
LexError(..) | Comment(..) => false,
|
LexError(..) | Comment(..) => false,
|
||||||
|
|
||||||
EOF => false,
|
EOF | NonToken => false,
|
||||||
|
|
||||||
_ => true,
|
_ => true,
|
||||||
}
|
}
|
||||||
@ -956,13 +960,6 @@ impl Token {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Is this token [`EOF`][Token::EOF]?
|
|
||||||
#[inline(always)]
|
|
||||||
#[must_use]
|
|
||||||
pub const fn is_eof(&self) -> bool {
|
|
||||||
matches!(self, Self::EOF)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// If another operator is after these, it's probably a unary operator
|
/// If another operator is after these, it's probably a unary operator
|
||||||
/// (not sure about `fn` name).
|
/// (not sure about `fn` name).
|
||||||
#[must_use]
|
#[must_use]
|
||||||
@ -2435,7 +2432,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
Some((Token::Reserved(s), pos)) => (match
|
Some((Token::Reserved(s), pos)) => (match
|
||||||
(s.as_str(),
|
(s.as_str(),
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
self.engine.custom_keywords.as_ref().map_or(false, |m| m.contains_key(&*s)),
|
self.engine.custom_keywords.as_deref().map_or(false, |m| m.contains_key(&*s)),
|
||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
false
|
false
|
||||||
)
|
)
|
||||||
@ -2472,7 +2469,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
#[cfg(feature = "no_custom_syntax")]
|
#[cfg(feature = "no_custom_syntax")]
|
||||||
(.., true) => unreachable!("no custom operators"),
|
(.., true) => unreachable!("no custom operators"),
|
||||||
// Reserved keyword that is not custom and disabled.
|
// Reserved keyword that is not custom and disabled.
|
||||||
(token, false) if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token)) => {
|
(token, false) if self.engine.disabled_symbols.as_deref().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())
|
||||||
},
|
},
|
||||||
@ -2481,13 +2478,13 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
}, pos),
|
}, pos),
|
||||||
// Custom keyword
|
// Custom keyword
|
||||||
#[cfg(not(feature = "no_custom_syntax"))]
|
#[cfg(not(feature = "no_custom_syntax"))]
|
||||||
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_ref().map_or(false,|m| m.contains_key(&*s)) => {
|
Some((Token::Identifier(s), pos)) if self.engine.custom_keywords.as_deref().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_ref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
|
Some((token, pos)) if token.is_literal() && self.engine.custom_keywords.as_deref().map_or(false,|m| m.contains_key(token.literal_syntax())) => {
|
||||||
if self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) {
|
if self.engine.disabled_symbols.as_deref().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 {
|
||||||
@ -2496,7 +2493,7 @@ impl<'a> Iterator for TokenIterator<'a> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Disabled symbol
|
// Disabled symbol
|
||||||
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_ref().map_or(false,|m| m.contains(token.literal_syntax())) => {
|
Some((token, pos)) if token.is_literal() && self.engine.disabled_symbols.as_deref().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
|
||||||
|
Loading…
Reference in New Issue
Block a user