rhai/src/engine.rs

339 lines
10 KiB
Rust
Raw Normal View History

2020-11-20 09:52:28 +01:00
//! Main module defining the script evaluation [`Engine`].
2016-02-29 22:43:45 +01:00
2021-12-27 15:02:34 +01:00
use crate::api::custom_syntax::CustomSyntax;
use crate::func::native::{OnDebugCallback, OnParseTokenCallback, OnPrintCallback, OnVarCallback};
2020-12-22 15:36:36 +01:00
use crate::packages::{Package, StandardPackage};
2021-11-13 15:36:23 +01:00
use crate::tokenizer::Token;
2022-01-07 05:19:01 +01:00
use crate::types::dynamic::{map_std_type_name, Union};
use crate::{
Dynamic, Identifier, ImmutableString, Module, Position, RhaiError, RhaiResult, Shared,
StaticVec, ERR,
};
2021-04-17 09:15:54 +02:00
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
2022-01-07 04:43:47 +01:00
any::type_name,
2021-03-23 05:13:53 +01:00
collections::{BTreeMap, BTreeSet},
2021-04-17 09:15:54 +02:00
fmt,
2022-01-07 04:43:47 +01:00
num::NonZeroU8,
2020-11-16 16:10:14 +01:00
};
2021-03-14 03:47:29 +01:00
pub type Precedence = NonZeroU8;
2021-12-27 16:15:25 +01: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 16:03:30 +01:00
2021-12-27 16:15:25 +01:00
/// Standard equality comparison operator.
2022-01-01 12:54:46 +01:00
///
/// Some standard functions (e.g. searching an [`Array`][crate::Array]) implicitly call this
/// function to compare two [`Dynamic`] values.
2021-12-27 16:15:25 +01:00
pub const OP_EQUALS: &str = Token::EqualsTo.literal_syntax();
2021-12-27 16:03:30 +01:00
2022-01-01 12:54:46 +01: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 16:15:25 +01:00
pub const OP_CONTAINS: &str = "contains";
2021-12-27 16:03:30 +01:00
2021-12-27 16:15:25 +01: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 04:43:47 +01: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.
pub(crate) global_sub_modules: BTreeMap<Identifier, Shared<Module>>,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// A module resolution service.
#[cfg(not(feature = "no_module"))]
pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// A map mapping type names to pretty-print names.
pub(crate) type_names: BTreeMap<Identifier, Identifier>,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// An empty [`ImmutableString`] for cloning purposes.
pub(crate) empty_string: ImmutableString,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// A set of symbols to disable.
pub(crate) disabled_symbols: BTreeSet<Identifier>,
/// A map containing custom keywords and precedence to recognize.
pub(crate) custom_keywords: BTreeMap<Identifier, Option<Precedence>>,
/// Custom syntax.
pub(crate) custom_syntax: BTreeMap<Identifier, Box<CustomSyntax>>,
/// Callback closure for resolving variable access.
pub(crate) resolve_var: Option<OnVarCallback>,
/// Callback closure to remap tokens during parsing.
pub(crate) token_mapper: Option<Box<OnParseTokenCallback>>,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// Callback closure for implementing the `print` command.
pub(crate) print: Option<OnPrintCallback>,
/// Callback closure for implementing the `debug` command.
pub(crate) debug: Option<OnDebugCallback>,
/// Callback closure for progress reporting.
#[cfg(not(feature = "unchecked"))]
pub(crate) progress: Option<crate::func::native::OnProgressCallback>,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// Optimize the [`AST`][crate::AST] after compilation.
#[cfg(not(feature = "no_optimize"))]
pub(crate) optimization_level: crate::OptimizationLevel,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// Language options.
pub(crate) options: crate::api::options::LanguageOptions,
2021-12-27 16:15:25 +01:00
2022-01-07 04:43:47 +01:00
/// Max limits.
#[cfg(not(feature = "unchecked"))]
pub(crate) limits: crate::api::limits::Limits,
2020-11-01 16:42:00 +01:00
}
2022-01-07 04:43:47 +01:00
impl fmt::Debug for Engine {
#[inline(always)]
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Engine")
2021-07-04 10:33:26 +02:00
}
}
2022-01-07 04:43:47 +01:00
impl Default for Engine {
2021-12-27 16:15:25 +01:00
#[inline(always)]
2022-01-07 04:43:47 +01:00
fn default() -> Self {
Self::new()
2021-07-04 10:33:26 +02:00
}
}
2022-01-07 04:43:47 +01: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 10:33:26 +02:00
}
2022-01-07 04:43:47 +01: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 17:18:41 +02:00
}
2022-01-07 04:43:47 +01:00
/// Is this function an anonymous function?
#[cfg(not(feature = "no_function"))]
#[inline(always)]
#[must_use]
pub fn is_anonymous_fn(fn_name: &str) -> bool {
fn_name.starts_with(FN_ANONYMOUS)
2021-12-27 16:15:25 +01:00
}
2021-12-15 05:06:17 +01:00
2022-01-07 04:43:47 +01:00
/// Print to `stdout`
#[inline]
#[allow(unused_variables)]
fn print_to_stdout(s: &str) {
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
println!("{}", s);
2020-10-15 17:30:30 +02:00
}
2022-01-07 04:43:47 +01:00
/// Debug to `stdout`
#[inline]
#[allow(unused_variables)]
fn debug_to_stdout(s: &str, source: Option<&str>, pos: Position) {
#[cfg(not(feature = "no_std"))]
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
if let Some(source) = source {
println!("{}{:?} | {}", source, pos, s);
} else if pos.is_none() {
println!("{}", s);
} else {
println!("{:?} | {}", pos, s);
2020-10-15 17:30:30 +02:00
}
2021-12-27 16:15:25 +01:00
}
2022-01-07 04:43:47 +01:00
impl Engine {
/// Create a new [`Engine`].
#[inline]
2021-06-12 16:47:43 +02:00
#[must_use]
2022-01-07 04:43:47 +01: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"))]
#[cfg(not(target_arch = "wasm32"))]
#[cfg(not(target_arch = "wasm64"))]
{
engine.module_resolver =
Some(Box::new(crate::module::resolvers::FileModuleResolver::new()));
2020-10-15 17:30:30 +02:00
}
2022-01-07 04:43:47 +01:00
// default print/debug implementations
engine.print = Some(Box::new(print_to_stdout));
engine.debug = Some(Box::new(debug_to_stdout));
engine.register_global_module(StandardPackage::new().as_shared_module());
engine
2020-10-15 17:30:30 +02:00
}
2022-01-07 04:43:47 +01: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 16:15:25 +01:00
#[inline]
#[must_use]
2022-01-07 04:43:47 +01:00
pub fn new_raw() -> Self {
let mut engine = Self {
global_modules: StaticVec::new_const(),
global_sub_modules: BTreeMap::new(),
#[cfg(not(feature = "no_module"))]
2022-01-07 04:43:47 +01:00
module_resolver: None,
2021-02-24 04:04:54 +01:00
2022-01-07 04:43:47 +01:00
type_names: BTreeMap::new(),
empty_string: ImmutableString::new(),
disabled_symbols: BTreeSet::new(),
custom_keywords: BTreeMap::new(),
custom_syntax: BTreeMap::new(),
2022-01-07 04:43:47 +01:00
resolve_var: None,
token_mapper: None,
2022-01-07 04:43:47 +01:00
print: None,
debug: None,
2022-01-06 15:10:16 +01:00
#[cfg(not(feature = "unchecked"))]
2022-01-07 04:43:47 +01:00
progress: None,
2022-01-07 04:43:47 +01:00
#[cfg(not(feature = "no_optimize"))]
optimization_level: crate::OptimizationLevel::default(),
2022-01-07 04:43:47 +01:00
options: crate::api::options::LanguageOptions::new(),
2016-02-29 22:43:45 +01:00
2022-01-07 04:43:47 +01:00
#[cfg(not(feature = "unchecked"))]
limits: crate::api::limits::Limits::new(),
};
2022-01-06 06:40:03 +01:00
2022-01-07 04:43:47 +01: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 06:40:03 +01:00
2022-01-07 04:43:47 +01:00
engine
2022-01-06 06:40:03 +01:00
}
2022-01-07 04:43:47 +01:00
/// Get an empty [`ImmutableString`].
///
2022-01-07 04:43:47 +01: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 06:40:03 +01:00
#[inline(always)]
2022-01-07 04:43:47 +01:00
#[must_use]
pub fn const_empty_string(&self) -> ImmutableString {
self.empty_string.clone()
}
2022-01-07 05:19:01 +01:00
/// Check a result to ensure that it is valid.
pub(crate) fn check_return_value(&self, mut result: RhaiResult, pos: Position) -> RhaiResult {
let _pos = pos;
match result {
Ok(ref mut r) => {
// 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();
}
return result;
}
}
#[cfg(not(feature = "unchecked"))]
self.check_data_size(&r, _pos)?;
}
_ => (),
}
result
}
2021-02-25 04:04:01 +01:00
/// Pretty-print a type name.
///
/// If a type is registered via [`register_type_with_name`][Engine::register_type_with_name],
/// the type name provided for the registration will be used.
#[inline]
2021-06-12 16:47:43 +02:00
#[must_use]
2021-02-25 04:04:01 +01:00
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
2020-04-11 12:09:03 +02:00
self.type_names
2020-10-25 14:57:18 +01:00
.get(name)
.map(|s| s.as_str())
2020-09-20 20:07:43 +02:00
.unwrap_or_else(|| map_std_type_name(name))
2020-03-02 16:16:19 +01:00
}
2020-10-05 07:45:57 +02:00
2021-12-27 05:27:31 +01:00
/// Make a `Box<`[`EvalAltResult<ErrorMismatchDataType>`][ERR::ErrorMismatchDataType]`>`.
#[inline]
2021-06-12 16:47:43 +02:00
#[must_use]
2021-12-25 16:49:14 +01:00
pub(crate) fn make_type_mismatch_err<T>(&self, typ: &str, pos: Position) -> RhaiError {
2021-12-27 05:27:31 +01:00
ERR::ErrorMismatchDataType(self.map_type_name(type_name::<T>()).into(), typ.into(), pos)
.into()
2020-10-05 07:45:57 +02:00
}
2016-03-01 15:40:48 +01:00
}