rhai/src/api/mod.rs

256 lines
8.1 KiB
Rust
Raw Normal View History

2021-11-13 15:36:23 +01:00
//! Module defining the public API of the Rhai engine.
2021-11-20 07:57:21 +01:00
pub mod eval;
2021-11-20 14:29:36 +01:00
pub mod run;
pub mod compile;
2021-11-20 07:57:21 +01:00
pub mod files;
2021-11-20 14:29:36 +01:00
pub mod register;
pub mod call_fn;
2021-12-03 04:16:35 +01:00
pub mod options;
2021-11-20 07:57:21 +01:00
pub mod limits;
2021-11-20 14:29:36 +01:00
pub mod events;
2021-12-27 15:02:34 +01:00
pub mod custom_syntax;
2021-11-20 14:29:36 +01:00
pub mod deprecated;
2021-11-20 07:57:21 +01:00
use crate::engine::Precedence;
use crate::tokenizer::Token;
use crate::{Engine, Identifier};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
2021-12-17 09:55:07 +01:00
pub mod default_limits {
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 8;
#[cfg(not(feature = "unchecked"))]
#[cfg(debug_assertions)]
pub const MAX_EXPR_DEPTH: usize = 32;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
#[cfg(debug_assertions)]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 16;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(debug_assertions))]
#[cfg(not(feature = "no_function"))]
pub const MAX_CALL_STACK_DEPTH: usize = 64;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(debug_assertions))]
pub const MAX_EXPR_DEPTH: usize = 64;
#[cfg(not(feature = "unchecked"))]
#[cfg(not(feature = "no_function"))]
#[cfg(not(debug_assertions))]
pub const MAX_FUNCTION_EXPR_DEPTH: usize = 32;
pub const MAX_DYNAMIC_PARAMETERS: usize = 16;
}
2021-11-20 07:57:21 +01:00
/// Script optimization API.
#[cfg(not(feature = "no_optimize"))]
impl Engine {
/// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation.
///
/// Not available under `no_optimize`.
#[inline(always)]
pub fn set_optimization_level(
&mut self,
optimization_level: crate::OptimizationLevel,
) -> &mut Self {
self.optimization_level = optimization_level;
self
}
/// The current optimization level.
/// It controls whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation.
///
/// Not available under `no_optimize`.
#[inline(always)]
#[must_use]
pub const fn optimization_level(&self) -> crate::OptimizationLevel {
self.optimization_level
}
2021-11-20 14:29:36 +01:00
/// Optimize the [`AST`][crate::AST] with constants defined in an external Scope. An optimized
/// copy of the [`AST`][crate::AST] is returned while the original [`AST`][crate::AST] is consumed.
2021-11-20 07:57:21 +01:00
///
/// Not available under `no_optimize`.
///
2021-11-20 14:29:36 +01:00
/// Although optimization is performed by default during compilation, sometimes it is necessary
/// to _re_-optimize an [`AST`][crate::AST]. For example, when working with constants that are
/// passed in via an external scope, it will be more efficient to optimize the
/// [`AST`][crate::AST] once again to take advantage of the new constants.
2021-11-20 07:57:21 +01:00
///
2021-11-20 14:29:36 +01:00
/// With this method, it is no longer necessary to recompile a large script. The script
/// [`AST`][crate::AST] can be compiled just once. Before evaluation, constants are passed into
/// the [`Engine`] via an external scope (i.e. with
/// [`Scope::push_constant`][crate::Scope::push_constant]). Then, the [`AST`][crate::AST] is
/// cloned and the copy re-optimized before running.
2021-11-20 07:57:21 +01:00
#[inline]
#[must_use]
pub fn optimize_ast(
&self,
scope: &crate::Scope,
ast: crate::AST,
optimization_level: crate::OptimizationLevel,
) -> crate::AST {
let mut ast = ast;
#[cfg(not(feature = "no_function"))]
let lib = ast
2021-11-28 15:57:28 +01:00
.shared_lib()
2021-11-20 07:57:21 +01:00
.iter_fn()
.filter(|f| f.func.is_script())
.map(|f| {
f.func
.get_script_fn_def()
2021-12-17 09:07:13 +01:00
.expect("script-defined function")
2021-11-20 07:57:21 +01:00
.clone()
})
.collect();
crate::optimizer::optimize_into_ast(
self,
scope,
2021-12-17 09:07:13 +01:00
ast.take_statements(),
#[cfg(not(feature = "no_function"))]
lib,
optimization_level,
)
2021-11-20 07:57:21 +01:00
}
}
impl Engine {
/// Set the module resolution service used by the [`Engine`].
///
/// Not available under `no_module`.
#[cfg(not(feature = "no_module"))]
#[inline(always)]
pub fn set_module_resolver(
&mut self,
resolver: impl crate::ModuleResolver + 'static,
) -> &mut Self {
self.module_resolver = Some(Box::new(resolver));
self
}
/// Disable a particular keyword or operator in the language.
///
/// # Examples
///
/// The following will raise an error during parsing because the `if` keyword is disabled
/// and is recognized as a reserved symbol!
///
/// ```rust,should_panic
/// # fn main() -> Result<(), rhai::ParseError> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
/// engine.disable_symbol("if"); // disable the 'if' keyword
///
/// engine.compile("let x = if true { 42 } else { 0 };")?;
/// // ^ 'if' is rejected as a reserved symbol
/// # Ok(())
/// # }
/// ```
///
/// The following will raise an error during parsing because the `+=` operator is disabled.
///
/// ```rust,should_panic
/// # fn main() -> Result<(), rhai::ParseError> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
/// engine.disable_symbol("+="); // disable the '+=' operator
///
/// engine.compile("let x = 42; x += 1;")?;
/// // ^ unknown operator
/// # Ok(())
/// # }
/// ```
#[inline(always)]
pub fn disable_symbol(&mut self, symbol: impl Into<Identifier>) -> &mut Self {
self.disabled_symbols.insert(symbol.into());
self
}
/// Register a custom operator with a precedence into the language.
///
2021-12-09 15:54:10 +01:00
/// The operator can be a valid identifier, a reserved symbol, a disabled operator or a disabled keyword.
2021-11-20 07:57:21 +01:00
///
/// The precedence cannot be zero.
///
/// # Example
///
/// ```rust
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
2021-12-09 15:54:10 +01:00
/// // Register a custom operator called '#' and give it
2021-11-20 07:57:21 +01:00
/// // a precedence of 160 (i.e. between +|- and *|/).
2021-12-09 15:54:10 +01:00
/// engine.register_custom_operator("#", 160).expect("should succeed");
2021-11-20 07:57:21 +01:00
///
2021-12-09 15:54:10 +01:00
/// // Register a binary function named '#'
/// engine.register_fn("#", |x: i64, y: i64| (x * y) - (x + y));
2021-11-20 07:57:21 +01:00
///
/// assert_eq!(
2021-12-09 15:54:10 +01:00
/// engine.eval_expression::<i64>("1 + 2 * 3 # 4 - 5 / 6")?,
2021-11-20 07:57:21 +01:00
/// 15
/// );
/// # Ok(())
/// # }
/// ```
pub fn register_custom_operator(
&mut self,
2022-01-04 08:22:48 +01:00
keyword: impl AsRef<str>,
2021-11-20 07:57:21 +01:00
precedence: u8,
) -> Result<&mut Self, String> {
let precedence = Precedence::new(precedence);
if precedence.is_none() {
return Err("precedence cannot be zero".into());
}
match Token::lookup_from_syntax(keyword.as_ref()) {
// Standard identifiers, reserved keywords and custom keywords are OK
None | Some(Token::Reserved(_)) | Some(Token::Custom(_)) => (),
// Active standard keywords cannot be made custom
// Disabled keywords are OK
Some(token) if token.is_standard_keyword() => {
if !self.disabled_symbols.contains(&*token.syntax()) {
return Err(format!("'{}' is a reserved keyword", keyword.as_ref()));
}
}
// Active standard symbols cannot be made custom
Some(token) if token.is_standard_symbol() => {
if !self.disabled_symbols.contains(&*token.syntax()) {
return Err(format!("'{}' is a reserved operator", keyword.as_ref()));
}
}
// Active standard symbols cannot be made custom
Some(token) if !self.disabled_symbols.contains(&*token.syntax()) => {
return Err(format!("'{}' is a reserved symbol", keyword.as_ref()))
}
// Disabled symbols are OK
Some(_) => (),
}
// Add to custom keywords
2022-01-04 08:22:48 +01:00
self.custom_keywords
.insert(keyword.as_ref().into(), precedence);
2021-11-20 07:57:21 +01:00
Ok(self)
}
}