2020-11-20 16:52:28 +08:00
|
|
|
//! Main module defining the script evaluation [`Engine`].
|
2016-02-29 16:43:45 -05:00
|
|
|
|
2022-05-19 21:40:22 +08:00
|
|
|
use crate::api::options::LangOptions;
|
2022-02-04 22:59:41 +08:00
|
|
|
use crate::func::native::{
|
|
|
|
OnDebugCallback, OnDefVarCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback,
|
|
|
|
};
|
2020-12-22 22:36:36 +08:00
|
|
|
use crate::packages::{Package, StandardPackage};
|
2021-11-13 22:36:23 +08:00
|
|
|
use crate::tokenizer::Token;
|
2022-03-29 08:18:20 +08:00
|
|
|
use crate::types::dynamic::Union;
|
2022-01-07 12:19:01 +08:00
|
|
|
use crate::{
|
2022-05-19 21:40:22 +08:00
|
|
|
Dynamic, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiResult, Shared,
|
|
|
|
StaticVec,
|
2022-01-07 12:19:01 +08:00
|
|
|
};
|
2021-04-17 15:15:54 +08:00
|
|
|
#[cfg(feature = "no_std")]
|
|
|
|
use std::prelude::v1::*;
|
|
|
|
use std::{
|
2021-03-23 12:13:53 +08:00
|
|
|
collections::{BTreeMap, BTreeSet},
|
2021-04-17 15:15:54 +08:00
|
|
|
fmt,
|
2022-01-07 11:43:47 +08:00
|
|
|
num::NonZeroU8,
|
2020-11-16 23:10:14 +08:00
|
|
|
};
|
2020-02-23 22:48:46 +08:00
|
|
|
|
2021-03-14 10:47:29 +08:00
|
|
|
pub type Precedence = NonZeroU8;
|
|
|
|
|
2021-12-27 23:15:25 +08:00
|
|
|
pub const KEYWORD_PRINT: &str = "print";
|
|
|
|
pub const KEYWORD_DEBUG: &str = "debug";
|
|
|
|
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
|
|
|
pub const KEYWORD_EVAL: &str = "eval";
|
|
|
|
pub const KEYWORD_FN_PTR: &str = "Fn";
|
|
|
|
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
|
|
|
pub const KEYWORD_FN_PTR_CURRY: &str = "curry";
|
|
|
|
#[cfg(not(feature = "no_closure"))]
|
|
|
|
pub const KEYWORD_IS_SHARED: &str = "is_shared";
|
|
|
|
pub const KEYWORD_IS_DEF_VAR: &str = "is_def_var";
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
pub const KEYWORD_IS_DEF_FN: &str = "is_def_fn";
|
|
|
|
pub const KEYWORD_THIS: &str = "this";
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
pub const KEYWORD_GLOBAL: &str = "global";
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
pub const FN_GET: &str = "get$";
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
pub const FN_SET: &str = "set$";
|
|
|
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
|
|
pub const FN_IDX_GET: &str = "index$get$";
|
|
|
|
#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))]
|
|
|
|
pub const FN_IDX_SET: &str = "index$set$";
|
|
|
|
#[cfg(not(feature = "no_function"))]
|
|
|
|
pub const FN_ANONYMOUS: &str = "anon$";
|
2021-12-27 23:03:30 +08:00
|
|
|
|
2021-12-27 23:15:25 +08:00
|
|
|
/// Standard equality comparison operator.
|
2022-01-01 19:54:46 +08:00
|
|
|
///
|
|
|
|
/// Some standard functions (e.g. searching an [`Array`][crate::Array]) implicitly call this
|
|
|
|
/// function to compare two [`Dynamic`] values.
|
2021-12-27 23:15:25 +08:00
|
|
|
pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax();
|
2021-12-27 23:03:30 +08:00
|
|
|
|
2022-01-01 19:54:46 +08:00
|
|
|
/// Standard concatenation operator.
|
|
|
|
///
|
|
|
|
/// Used primarily to build up interpolated strings.
|
|
|
|
pub const OP_CONCAT: &str = Token::PlusAssign.literal_syntax();
|
|
|
|
|
|
|
|
/// Standard containment testing function.
|
|
|
|
///
|
|
|
|
/// The `in` operator is implemented as a call to this function.
|
2021-12-27 23:15:25 +08:00
|
|
|
pub const OP_CONTAINS: &str = "contains";
|
2021-12-27 23:03:30 +08:00
|
|
|
|
2021-12-27 23:15:25 +08:00
|
|
|
/// Standard exclusive range operator.
|
|
|
|
pub const OP_EXCLUSIVE_RANGE: &str = Token::ExclusiveRange.literal_syntax();
|
|
|
|
|
|
|
|
/// Standard inclusive range operator.
|
|
|
|
pub const OP_INCLUSIVE_RANGE: &str = Token::InclusiveRange.literal_syntax();
|
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Rhai main scripting engine.
|
|
|
|
///
|
|
|
|
/// # Thread Safety
|
|
|
|
///
|
|
|
|
/// [`Engine`] is re-entrant.
|
|
|
|
///
|
|
|
|
/// Currently, [`Engine`] is neither [`Send`] nor [`Sync`].
|
|
|
|
/// Use the `sync` feature to make it [`Send`] `+` [`Sync`].
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let result = engine.eval::<i64>("40 + 2")?;
|
|
|
|
///
|
|
|
|
/// println!("Answer: {}", result); // prints 42
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
pub struct Engine {
|
|
|
|
/// A collection of all modules loaded into the global namespace of the Engine.
|
|
|
|
pub(crate) global_modules: StaticVec<Shared<Module>>,
|
|
|
|
/// A collection of all sub-modules directly loaded into the Engine.
|
2022-01-29 11:09:43 +08:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
pub(crate) global_sub_modules: BTreeMap<Identifier, Shared<Module>>,
|
2021-12-27 23:15:25 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// A module resolution service.
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-03-20 21:58:43 +08:00
|
|
|
pub(crate) module_resolver: Box<dyn crate::ModuleResolver>,
|
2021-12-27 23:15:25 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// An empty [`ImmutableString`] for cloning purposes.
|
|
|
|
pub(crate) empty_string: ImmutableString,
|
2021-12-27 23:15:25 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// A set of symbols to disable.
|
|
|
|
pub(crate) disabled_symbols: BTreeSet<Identifier>,
|
|
|
|
/// A map containing custom keywords and precedence to recognize.
|
2022-07-05 22:59:03 +08:00
|
|
|
#[cfg(not(feature = "no_custom_syntax"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
pub(crate) custom_keywords: BTreeMap<Identifier, Option<Precedence>>,
|
|
|
|
/// Custom syntax.
|
2022-07-05 22:59:03 +08:00
|
|
|
#[cfg(not(feature = "no_custom_syntax"))]
|
|
|
|
pub(crate) custom_syntax: BTreeMap<Identifier, crate::api::custom_syntax::CustomSyntax>,
|
2022-02-04 22:59:41 +08:00
|
|
|
/// Callback closure for filtering variable definition.
|
|
|
|
pub(crate) def_var_filter: Option<Box<OnDefVarCallback>>,
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Callback closure for resolving variable access.
|
2022-01-27 23:55:32 +08:00
|
|
|
pub(crate) resolve_var: Option<Box<OnVarCallback>>,
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Callback closure to remap tokens during parsing.
|
|
|
|
pub(crate) token_mapper: Option<Box<OnParseTokenCallback>>,
|
2021-12-27 23:15:25 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Callback closure for implementing the `print` command.
|
2022-03-20 21:58:43 +08:00
|
|
|
pub(crate) print: Box<OnPrintCallback>,
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Callback closure for implementing the `debug` command.
|
2022-03-20 21:58:43 +08:00
|
|
|
pub(crate) debug: Box<OnDebugCallback>,
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Callback closure for progress reporting.
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
2022-01-27 23:55:32 +08:00
|
|
|
pub(crate) progress: Option<Box<crate::func::native::OnProgressCallback>>,
|
2021-12-27 23:15:25 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Language options.
|
2022-05-19 21:40:22 +08:00
|
|
|
pub(crate) options: LangOptions,
|
|
|
|
|
2022-05-21 21:44:12 +08:00
|
|
|
/// Default value for the custom state.
|
|
|
|
pub(crate) def_tag: Dynamic,
|
|
|
|
|
2022-05-19 21:40:22 +08:00
|
|
|
/// Script optimization level.
|
2022-05-21 21:44:12 +08:00
|
|
|
pub(crate) optimization_level: OptimizationLevel,
|
2021-12-27 23:15:25 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Max limits.
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
pub(crate) limits: crate::api::limits::Limits,
|
2022-01-24 17:04:40 +08:00
|
|
|
|
|
|
|
/// Callback closure for debugging.
|
|
|
|
#[cfg(feature = "debugging")]
|
2022-01-28 18:59:18 +08:00
|
|
|
pub(crate) debugger: Option<(
|
|
|
|
Box<crate::eval::OnDebuggingInit>,
|
|
|
|
Box<crate::eval::OnDebuggerCallback>,
|
|
|
|
)>,
|
2020-11-01 23:42:00 +08:00
|
|
|
}
|
2020-06-27 23:56:24 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
impl fmt::Debug for Engine {
|
2022-01-10 22:51:04 +08:00
|
|
|
#[inline]
|
2022-01-07 11:43:47 +08:00
|
|
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
2022-01-10 22:51:04 +08:00
|
|
|
let mut f = f.debug_struct("Engine");
|
|
|
|
|
2022-01-29 11:09:43 +08:00
|
|
|
f.field("global_modules", &self.global_modules);
|
2022-01-10 22:51:04 +08:00
|
|
|
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-03-20 21:58:43 +08:00
|
|
|
f.field("global_sub_modules", &self.global_sub_modules);
|
2022-01-10 22:51:04 +08:00
|
|
|
|
2022-07-05 22:59:03 +08:00
|
|
|
f.field("disabled_symbols", &self.disabled_symbols);
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_custom_syntax"))]
|
|
|
|
f.field("custom_keywords", &self.custom_keywords).field(
|
|
|
|
"custom_syntax",
|
|
|
|
&self
|
|
|
|
.custom_syntax
|
|
|
|
.keys()
|
|
|
|
.map(|s| s.as_str())
|
|
|
|
.collect::<String>(),
|
|
|
|
);
|
|
|
|
|
|
|
|
f.field("def_var_filter", &self.def_var_filter.is_some())
|
2022-01-10 22:51:04 +08:00
|
|
|
.field("resolve_var", &self.resolve_var.is_some())
|
2022-03-20 21:58:43 +08:00
|
|
|
.field("token_mapper", &self.token_mapper.is_some());
|
2022-01-10 22:51:04 +08:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
f.field("progress", &self.progress.is_some());
|
|
|
|
|
|
|
|
f.field("options", &self.options);
|
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
f.field("limits", &self.limits);
|
|
|
|
|
|
|
|
f.finish()
|
2021-07-04 16:33:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
impl Default for Engine {
|
2021-12-27 23:15:25 +08:00
|
|
|
#[inline(always)]
|
2022-01-07 11:43:47 +08:00
|
|
|
fn default() -> Self {
|
|
|
|
Self::new()
|
2021-07-04 16:33:26 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Make getter function
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn make_getter(id: &str) -> Identifier {
|
|
|
|
let mut buf = Identifier::new_const();
|
|
|
|
buf.push_str(FN_GET);
|
|
|
|
buf.push_str(id);
|
|
|
|
buf
|
2021-07-04 16:33:26 +08:00
|
|
|
}
|
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Make setter function
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
#[inline(always)]
|
|
|
|
#[must_use]
|
|
|
|
pub fn make_setter(id: &str) -> Identifier {
|
|
|
|
let mut buf = Identifier::new_const();
|
|
|
|
buf.push_str(FN_SET);
|
|
|
|
buf.push_str(id);
|
|
|
|
buf
|
2021-04-06 23:18:41 +08:00
|
|
|
}
|
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
impl Engine {
|
|
|
|
/// Create a new [`Engine`].
|
|
|
|
#[inline]
|
2021-06-12 22:47:43 +08:00
|
|
|
#[must_use]
|
2022-01-07 11:43:47 +08:00
|
|
|
pub fn new() -> Self {
|
|
|
|
// Create the new scripting Engine
|
|
|
|
let mut engine = Self::new_raw();
|
|
|
|
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
|
|
|
#[cfg(not(feature = "no_std"))]
|
2022-01-12 08:12:28 +08:00
|
|
|
#[cfg(not(target_family = "wasm"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
{
|
2022-03-20 21:58:43 +08:00
|
|
|
engine.module_resolver = Box::new(crate::module::resolvers::FileModuleResolver::new());
|
2020-10-15 23:30:30 +08:00
|
|
|
}
|
2022-01-07 11:43:47 +08:00
|
|
|
|
|
|
|
// default print/debug implementations
|
2022-01-12 08:12:28 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
|
|
|
#[cfg(not(target_family = "wasm"))]
|
|
|
|
{
|
2022-03-20 21:58:43 +08:00
|
|
|
engine.print = Box::new(|s| println!("{}", s));
|
|
|
|
engine.debug = Box::new(|s, source, pos| {
|
2022-01-12 08:12:28 +08:00
|
|
|
if let Some(source) = source {
|
2022-01-24 17:04:40 +08:00
|
|
|
println!("{} @ {:?} | {}", source, pos, s);
|
2022-01-12 08:12:28 +08:00
|
|
|
} else if pos.is_none() {
|
|
|
|
println!("{}", s);
|
|
|
|
} else {
|
|
|
|
println!("{:?} | {}", pos, s);
|
|
|
|
}
|
2022-03-20 21:58:43 +08:00
|
|
|
});
|
2022-01-12 08:12:28 +08:00
|
|
|
}
|
2022-01-07 11:43:47 +08:00
|
|
|
|
|
|
|
engine.register_global_module(StandardPackage::new().as_shared_module());
|
|
|
|
|
|
|
|
engine
|
2020-10-15 23:30:30 +08:00
|
|
|
}
|
2022-01-07 11:43:47 +08:00
|
|
|
|
|
|
|
/// Create a new [`Engine`] with minimal built-in functions.
|
|
|
|
///
|
|
|
|
/// Use [`register_global_module`][Engine::register_global_module] to add packages of functions.
|
2021-12-27 23:15:25 +08:00
|
|
|
#[inline]
|
|
|
|
#[must_use]
|
2022-01-07 11:43:47 +08:00
|
|
|
pub fn new_raw() -> Self {
|
|
|
|
let mut engine = Self {
|
|
|
|
global_modules: StaticVec::new_const(),
|
2022-01-29 11:09:43 +08:00
|
|
|
|
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
global_sub_modules: BTreeMap::new(),
|
2021-03-03 11:40:27 +08:00
|
|
|
|
2021-02-07 17:06:33 +08:00
|
|
|
#[cfg(not(feature = "no_module"))]
|
2022-03-20 21:58:43 +08:00
|
|
|
module_resolver: Box::new(crate::module::resolvers::DummyModuleResolver::new()),
|
2021-02-24 11:04:54 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
empty_string: ImmutableString::new(),
|
|
|
|
disabled_symbols: BTreeSet::new(),
|
2022-07-05 22:59:03 +08:00
|
|
|
#[cfg(not(feature = "no_custom_syntax"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
custom_keywords: BTreeMap::new(),
|
2022-07-05 22:59:03 +08:00
|
|
|
#[cfg(not(feature = "no_custom_syntax"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
custom_syntax: BTreeMap::new(),
|
2022-01-06 16:06:31 +08:00
|
|
|
|
2022-02-04 22:59:41 +08:00
|
|
|
def_var_filter: None,
|
2022-01-07 11:43:47 +08:00
|
|
|
resolve_var: None,
|
|
|
|
token_mapper: None,
|
2022-01-06 16:06:31 +08:00
|
|
|
|
2022-03-20 21:58:43 +08:00
|
|
|
print: Box::new(|_| {}),
|
|
|
|
debug: Box::new(|_, _, _| {}),
|
2022-01-06 16:06:31 +08:00
|
|
|
|
2022-01-06 22:10:16 +08:00
|
|
|
#[cfg(not(feature = "unchecked"))]
|
2022-01-07 11:43:47 +08:00
|
|
|
progress: None,
|
2020-10-18 17:02:17 +08:00
|
|
|
|
2022-05-19 21:40:22 +08:00
|
|
|
options: LangOptions::new(),
|
|
|
|
|
2022-05-21 21:44:12 +08:00
|
|
|
def_tag: Dynamic::UNIT,
|
|
|
|
|
2022-05-19 21:40:22 +08:00
|
|
|
#[cfg(not(feature = "no_optimize"))]
|
|
|
|
optimization_level: OptimizationLevel::Simple,
|
|
|
|
#[cfg(feature = "no_optimize")]
|
|
|
|
optimization_level: (),
|
2016-02-29 16:43:45 -05:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
limits: crate::api::limits::Limits::new(),
|
2022-01-24 17:04:40 +08:00
|
|
|
|
|
|
|
#[cfg(feature = "debugging")]
|
|
|
|
debugger: None,
|
2022-01-07 11:43:47 +08:00
|
|
|
};
|
2022-01-06 13:40:03 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
// Add the global namespace module
|
|
|
|
let mut global_namespace = Module::new();
|
|
|
|
global_namespace.internal = true;
|
|
|
|
engine.global_modules.push(global_namespace.into());
|
2022-01-06 13:40:03 +08:00
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
engine
|
2022-01-06 13:40:03 +08:00
|
|
|
}
|
|
|
|
|
2022-01-07 11:43:47 +08:00
|
|
|
/// Get an empty [`ImmutableString`].
|
2022-01-06 15:28:53 +08:00
|
|
|
///
|
2022-01-07 11:43:47 +08:00
|
|
|
/// [`Engine`] keeps a single instance of an empty [`ImmutableString`] and uses this to create
|
|
|
|
/// shared instances for subsequent uses. This minimizes unnecessary allocations for empty strings.
|
2022-01-06 13:40:03 +08:00
|
|
|
#[inline(always)]
|
2022-01-07 11:43:47 +08:00
|
|
|
#[must_use]
|
|
|
|
pub fn const_empty_string(&self) -> ImmutableString {
|
|
|
|
self.empty_string.clone()
|
2020-05-15 11:43:32 +08:00
|
|
|
}
|
|
|
|
|
2022-01-07 12:19:01 +08:00
|
|
|
/// Check a result to ensure that it is valid.
|
2022-04-11 16:29:16 +08:00
|
|
|
pub(crate) fn check_return_value(&self, mut result: RhaiResult, _pos: Position) -> RhaiResult {
|
2022-07-27 16:04:24 +08:00
|
|
|
if let Ok(ref mut r) = result {
|
|
|
|
// Concentrate all empty strings into one instance to save memory
|
|
|
|
if let Dynamic(Union::Str(s, ..)) = r {
|
|
|
|
if s.is_empty() {
|
|
|
|
if !s.ptr_eq(&self.empty_string) {
|
|
|
|
*s = self.const_empty_string();
|
2022-01-07 12:19:01 +08:00
|
|
|
}
|
2022-07-27 16:04:24 +08:00
|
|
|
return result;
|
2022-01-07 12:19:01 +08:00
|
|
|
}
|
|
|
|
}
|
2022-07-27 16:04:24 +08:00
|
|
|
|
|
|
|
#[cfg(not(feature = "unchecked"))]
|
|
|
|
self.check_data_size(r, _pos)?;
|
2022-01-07 12:19:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
result
|
|
|
|
}
|
2016-03-01 09:40:48 -05:00
|
|
|
}
|