rhai/src/api.rs

964 lines
31 KiB
Rust
Raw Normal View History

2020-03-08 12:54:02 +01:00
//! Module that defines the extern API of `Engine`.
2020-03-04 15:00:01 +01:00
use crate::any::{Any, AnyExt, Dynamic};
use crate::call::FuncArgs;
2020-03-30 10:10:50 +02:00
use crate::engine::{make_getter, make_setter, Engine, FnAny, FnSpec};
2020-03-04 15:00:01 +01:00
use crate::error::ParseError;
use crate::fn_register::RegisterFn;
2020-03-22 14:03:58 +01:00
use crate::parser::{lex, parse, parse_global_expr, FnDef, Position, AST};
2020-03-04 15:00:01 +01:00
use crate::result::EvalAltResult;
2020-03-03 08:20:20 +01:00
use crate::scope::Scope;
#[cfg(not(feature = "no_optimize"))]
2020-03-18 11:41:18 +01:00
use crate::optimize::optimize_into_ast;
2020-03-17 19:26:11 +01:00
use crate::stdlib::{
2020-03-10 03:07:44 +01:00
any::{type_name, TypeId},
2020-03-17 19:26:11 +01:00
boxed::Box,
string::{String, ToString},
sync::Arc,
2020-03-17 19:26:11 +01:00
vec::Vec,
2020-03-10 03:07:44 +01:00
};
#[cfg(not(feature = "no_std"))]
2020-03-17 19:26:11 +01:00
use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
impl<'e> Engine<'e> {
2020-03-19 06:52:10 +01:00
/// Register a custom function.
2020-03-30 16:19:37 +02:00
pub(crate) fn register_fn_raw(&mut self, fn_name: &str, args: Vec<TypeId>, f: Box<FnAny>) {
2020-03-04 15:00:01 +01:00
let spec = FnSpec {
name: fn_name.to_string().into(),
args,
};
self.functions.insert(spec, f);
2020-03-04 15:00:01 +01:00
}
/// Register a custom type for use with the `Engine`.
2020-03-19 06:52:10 +01:00
/// The type must implement `Clone`.
///
/// # Example
///
/// ```
2020-03-30 10:10:50 +02:00
/// #[derive(Debug, Clone, Eq, PartialEq)]
2020-03-19 06:52:10 +01:00
/// struct TestStruct {
/// field: i64
/// }
///
/// impl TestStruct {
2020-03-22 03:18:16 +01:00
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn update(&mut self, offset: i64) { self.field += offset; }
2020-03-19 06:52:10 +01:00
/// }
///
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, RegisterFn};
///
/// let mut engine = Engine::new();
///
/// // Register the custom type.
/// engine.register_type::<TestStruct>();
///
/// engine.register_fn("new_ts", TestStruct::new);
///
2020-03-22 03:18:16 +01:00
/// // Use `register_fn` to register methods on the type.
2020-03-19 06:52:10 +01:00
/// engine.register_fn("update", TestStruct::update);
///
/// assert_eq!(
2020-03-30 10:10:50 +02:00
/// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?,
/// TestStruct { field: 42 }
2020-03-19 06:52:10 +01:00
/// );
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_object"))]
2020-03-04 15:00:01 +01:00
pub fn register_type<T: Any + Clone>(&mut self) {
2020-03-08 12:54:02 +01:00
self.register_type_with_name::<T>(type_name::<T>());
2020-03-04 15:00:01 +01:00
}
2020-03-19 06:52:10 +01:00
/// Register a custom type for use with the `Engine`, with a pretty-print name
/// for the `type_of` function. The type must implement `Clone`.
///
/// # Example
///
/// ```
/// #[derive(Clone)]
2020-03-30 10:10:50 +02:00
/// struct TestStruct {
2020-03-19 06:52:10 +01:00
/// field: i64
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// }
///
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, RegisterFn};
///
/// let mut engine = Engine::new();
///
/// // Register the custom type.
/// engine.register_type::<TestStruct>();
///
/// engine.register_fn("new_ts", TestStruct::new);
///
/// assert_eq!(
/// engine.eval::<String>("let x = new_ts(); type_of(x)")?,
/// "rust_out::TestStruct"
/// );
///
/// // Register the custom type with a name.
/// engine.register_type_with_name::<TestStruct>("Hello");
///
/// // Register methods on the type.
/// engine.register_fn("new_ts", TestStruct::new);
///
/// assert_eq!(
/// engine.eval::<String>("let x = new_ts(); type_of(x)")?,
/// "Hello"
/// );
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_object"))]
2020-03-08 12:54:02 +01:00
pub fn register_type_with_name<T: Any + Clone>(&mut self, name: &str) {
2020-03-04 15:00:01 +01:00
// Add the pretty-print type name into the map
2020-03-08 12:54:02 +01:00
self.type_names
.insert(type_name::<T>().to_string(), name.to_string());
2020-03-04 15:00:01 +01:00
}
/// Register an iterator adapter for a type with the `Engine`.
2020-03-19 06:52:10 +01:00
/// This is an advanced feature.
2020-03-04 15:00:01 +01:00
pub fn register_iterator<T: Any, F>(&mut self, f: F)
where
F: Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static,
{
2020-03-11 16:43:04 +01:00
self.type_iterators.insert(TypeId::of::<T>(), Box::new(f));
2020-03-04 15:00:01 +01:00
}
/// Register a getter function for a member of a registered type with the `Engine`.
2020-03-19 06:52:10 +01:00
///
2020-03-22 03:18:16 +01:00
/// The function signature must start with `&mut self` and not `&self`.
///
2020-03-19 06:52:10 +01:00
/// # Example
///
/// ```
/// #[derive(Clone)]
2020-03-30 10:10:50 +02:00
/// struct TestStruct {
2020-03-19 06:52:10 +01:00
/// field: i64
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
2020-03-22 03:18:16 +01:00
///
/// // Even a getter must start with `&mut self` and not `&self`.
2020-03-19 06:52:10 +01:00
/// fn get_field(&mut self) -> i64 { self.field }
/// }
///
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, RegisterFn};
///
/// let mut engine = Engine::new();
///
/// // Register the custom type.
/// engine.register_type::<TestStruct>();
///
/// engine.register_fn("new_ts", TestStruct::new);
///
/// // Register a getter on a property (notice it doesn't have to be the same name).
/// engine.register_get("xyz", TestStruct::get_field);
///
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a.xyz")?, 1);
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_object"))]
2020-03-04 15:00:01 +01:00
pub fn register_get<T: Any + Clone, U: Any + Clone>(
&mut self,
name: &str,
2020-03-04 16:06:05 +01:00
callback: impl Fn(&mut T) -> U + 'static,
2020-03-04 15:00:01 +01:00
) {
2020-03-30 16:19:37 +02:00
self.register_fn(&make_getter(name), callback);
2020-03-04 15:00:01 +01:00
}
/// Register a setter function for a member of a registered type with the `Engine`.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```
2020-03-30 10:10:50 +02:00
/// #[derive(Debug, Clone, Eq, PartialEq)]
2020-03-19 06:52:10 +01:00
/// struct TestStruct {
/// field: i64
/// }
///
/// impl TestStruct {
/// fn new() -> Self { TestStruct { field: 1 } }
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
/// }
///
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, RegisterFn};
///
/// let mut engine = Engine::new();
///
/// // Register the custom type.
/// engine.register_type::<TestStruct>();
///
/// engine.register_fn("new_ts", TestStruct::new);
///
/// // Register a setter on a property (notice it doesn't have to be the same name)
/// engine.register_set("xyz", TestStruct::set_field);
///
/// // Notice that, with a getter, there is no way to get the property value
2020-03-30 10:10:50 +02:00
/// assert_eq!(
/// engine.eval::<TestStruct>("let a = new_ts(); a.xyz = 42; a")?,
/// TestStruct { field: 42 }
/// );
2020-03-19 06:52:10 +01:00
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_object"))]
2020-03-04 15:00:01 +01:00
pub fn register_set<T: Any + Clone, U: Any + Clone>(
&mut self,
name: &str,
2020-03-04 16:06:05 +01:00
callback: impl Fn(&mut T, U) -> () + 'static,
2020-03-04 15:00:01 +01:00
) {
2020-03-30 16:19:37 +02:00
self.register_fn(&make_setter(name), callback);
2020-03-04 15:00:01 +01:00
}
/// Shorthand for registering both getter and setter functions
/// of a registered type with the `Engine`.
2020-03-19 06:52:10 +01:00
///
2020-03-22 03:18:16 +01:00
/// All function signatures must start with `&mut self` and not `&self`.
///
2020-03-19 06:52:10 +01:00
/// # Example
///
/// ```
/// #[derive(Clone)]
/// struct TestStruct {
/// field: i64
/// }
///
/// impl TestStruct {
2020-03-22 03:18:16 +01:00
/// fn new() -> Self { TestStruct { field: 1 } }
2020-03-19 06:52:10 +01:00
/// fn get_field(&mut self) -> i64 { self.field }
2020-03-22 03:18:16 +01:00
/// // Even a getter must start with `&mut self` and not `&self`.
2020-03-19 06:52:10 +01:00
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
/// }
///
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, RegisterFn};
///
/// let mut engine = Engine::new();
///
/// // Register the custom type.
/// engine.register_type::<TestStruct>();
///
/// engine.register_fn("new_ts", TestStruct::new);
///
/// // Register a getter and a setter on a property
/// // (notice it doesn't have to be the same name)
/// engine.register_get_set("xyz", TestStruct::get_field, TestStruct::set_field);
///
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a.xyz = 42; a.xyz")?, 42);
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_object"))]
2020-03-04 15:00:01 +01:00
pub fn register_get_set<T: Any + Clone, U: Any + Clone>(
&mut self,
name: &str,
get_fn: impl Fn(&mut T) -> U + 'static,
set_fn: impl Fn(&mut T, U) -> () + 'static,
) {
self.register_get(name, get_fn);
self.register_set(name, set_fn);
}
2020-03-19 12:53:42 +01:00
/// Compile a string into an `AST`, which can be used later for evaluation.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
2020-03-19 12:53:42 +01:00
/// // Compile a script to an AST and store it for later evaluation
2020-03-19 06:52:10 +01:00
/// let ast = engine.compile("40 + 2")?;
///
/// for _ in 0..42 {
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
/// }
/// # Ok(())
/// # }
/// ```
2020-03-09 14:57:07 +01:00
pub fn compile(&self, input: &str) -> Result<AST, ParseError> {
self.compile_with_scope(&Scope::new(), input)
}
2020-03-19 12:53:42 +01:00
/// Compile a string into an `AST` using own scope, which can be used later for evaluation.
/// The scope is useful for passing constants into the script for optimization
/// when using `OptimizationLevel::Full`.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
2020-03-19 12:53:42 +01:00
/// # #[cfg(not(feature = "no_optimize"))]
/// # {
/// use rhai::{Engine, Scope, OptimizationLevel};
2020-03-19 06:52:10 +01:00
///
/// let mut engine = Engine::new();
///
2020-03-19 12:53:42 +01:00
/// // Set optimization level to 'Full' so the Engine can fold constants
/// // into function calls and operators.
/// engine.set_optimization_level(OptimizationLevel::Full);
///
2020-03-19 06:52:10 +01:00
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push_constant("x", 42_i64); // 'x' is a constant
///
2020-03-19 12:53:42 +01:00
/// // Compile a script to an AST and store it for later evaluation.
/// // Notice that `Full` optimization is on, so constants are folded
/// // into function calls and operators.
2020-03-19 06:52:10 +01:00
/// let ast = engine.compile_with_scope(&mut scope,
2020-03-19 12:53:42 +01:00
/// "if x > 40 { x } else { 0 }" // all 'x' are replaced with 42
2020-03-19 06:52:10 +01:00
/// )?;
///
2020-03-19 12:53:42 +01:00
/// // Normally this would have failed because no scope is passed into the 'eval_ast'
/// // call and so the variable 'x' does not exist. Here, it passes because the script
/// // has been optimized and all references to 'x' are already gone.
2020-03-19 06:52:10 +01:00
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
2020-03-19 12:53:42 +01:00
/// # }
2020-03-19 06:52:10 +01:00
/// # Ok(())
/// # }
/// ```
pub fn compile_with_scope(&self, scope: &Scope, input: &str) -> Result<AST, ParseError> {
let tokens_stream = lex(input);
parse(&mut tokens_stream.peekable(), self, scope)
}
2020-03-19 06:52:10 +01:00
/// Read the contents of a file into a string.
#[cfg(not(feature = "no_std"))]
2020-03-13 11:27:53 +01:00
fn read_file(path: PathBuf) -> Result<String, EvalAltResult> {
2020-03-13 11:07:51 +01:00
let mut f = File::open(path.clone())
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(path.clone(), err))?;
let mut contents = String::new();
f.read_to_string(&mut contents)
2020-03-30 16:19:37 +02:00
.map_err(|err| EvalAltResult::ErrorReadingScriptFile(path.clone(), err))?;
Ok(contents)
}
2020-03-19 12:53:42 +01:00
/// Compile a script file into an `AST`, which can be used later for evaluation.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```no_run
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
2020-03-19 12:53:42 +01:00
/// // Compile a script file to an AST and store it for later evaluation.
2020-03-19 06:52:10 +01:00
/// // Notice that a PathBuf is required which can easily be constructed from a string.
/// let ast = engine.compile_file("script.rhai".into())?;
///
/// for _ in 0..42 {
/// engine.eval_ast::<i64>(&ast)?;
/// }
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_std"))]
2020-03-13 11:27:53 +01:00
pub fn compile_file(&self, path: PathBuf) -> Result<AST, EvalAltResult> {
2020-03-14 07:33:56 +01:00
self.compile_file_with_scope(&Scope::new(), path)
}
2020-03-19 12:53:42 +01:00
/// Compile a script file into an `AST` using own scope, which can be used later for evaluation.
/// The scope is useful for passing constants into the script for optimization
/// when using `OptimizationLevel::Full`.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```no_run
/// # fn main() -> Result<(), rhai::EvalAltResult> {
2020-03-19 12:53:42 +01:00
/// # #[cfg(not(feature = "no_optimize"))]
/// # {
/// use rhai::{Engine, Scope, OptimizationLevel};
2020-03-19 06:52:10 +01:00
///
/// let mut engine = Engine::new();
///
2020-03-19 12:53:42 +01:00
/// // Set optimization level to 'Full' so the Engine can fold constants.
/// engine.set_optimization_level(OptimizationLevel::Full);
///
2020-03-19 06:52:10 +01:00
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push_constant("x", 42_i64); // 'x' is a constant
///
2020-03-19 12:53:42 +01:00
/// // Compile a script to an AST and store it for later evaluation.
2020-03-19 06:52:10 +01:00
/// // Notice that a PathBuf is required which can easily be constructed from a string.
/// let ast = engine.compile_file_with_scope(&mut scope, "script.rhai".into())?;
///
/// let result = engine.eval_ast::<i64>(&ast)?;
2020-03-19 12:53:42 +01:00
/// # }
2020-03-19 06:52:10 +01:00
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_std"))]
pub fn compile_file_with_scope(
&self,
scope: &Scope,
2020-03-14 07:33:56 +01:00
path: PathBuf,
) -> Result<AST, EvalAltResult> {
2020-03-14 07:33:56 +01:00
Self::read_file(path).and_then(|contents| {
self.compile_with_scope(scope, &contents)
.map_err(|err| err.into())
})
}
2020-03-22 14:03:58 +01:00
/// Compile a string containing an expression into an `AST`,
/// which can be used later for evaluation.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
/// // Compile a script to an AST and store it for later evaluation
/// let ast = engine.compile_expression("40 + 2")?;
///
/// for _ in 0..42 {
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
/// }
/// # Ok(())
/// # }
/// ```
pub fn compile_expression(&self, input: &str) -> Result<AST, ParseError> {
self.compile_expression_with_scope(&Scope::new(), input)
}
/// Compile a string containing an expression into an `AST` using own scope,
/// which can be used later for evaluation.
///
/// The scope is useful for passing constants into the script for optimization
/// when using `OptimizationLevel::Full`.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # #[cfg(not(feature = "no_optimize"))]
/// # {
/// use rhai::{Engine, Scope, OptimizationLevel};
///
/// let mut engine = Engine::new();
///
/// // Set optimization level to 'Full' so the Engine can fold constants
/// // into function calls and operators.
/// engine.set_optimization_level(OptimizationLevel::Full);
///
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push_constant("x", 10_i64); // 'x' is a constant
///
/// // Compile a script to an AST and store it for later evaluation.
/// // Notice that `Full` optimization is on, so constants are folded
/// // into function calls and operators.
/// let ast = engine.compile_expression_with_scope(&mut scope,
/// "2 + (x + x) * 2" // all 'x' are replaced with 10
/// )?;
///
/// // Normally this would have failed because no scope is passed into the 'eval_ast'
/// // call and so the variable 'x' does not exist. Here, it passes because the script
/// // has been optimized and all references to 'x' are already gone.
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
/// # }
/// # Ok(())
/// # }
/// ```
pub fn compile_expression_with_scope(
&self,
scope: &Scope,
input: &str,
) -> Result<AST, ParseError> {
let tokens_stream = lex(input);
parse_global_expr(&mut tokens_stream.peekable(), self, scope)
}
2020-03-19 06:52:10 +01:00
/// Evaluate a script file.
///
/// # Example
///
/// ```no_run
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
/// // Notice that a PathBuf is required which can easily be constructed from a string.
/// let result = engine.eval_file::<i64>("script.rhai".into())?;
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_std"))]
2020-03-13 11:07:51 +01:00
pub fn eval_file<T: Any + Clone>(&mut self, path: PathBuf) -> Result<T, EvalAltResult> {
2020-03-13 11:27:53 +01:00
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
}
2020-03-19 06:52:10 +01:00
/// Evaluate a script file with own scope.
///
/// # Example
///
/// ```no_run
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope};
///
/// let mut engine = Engine::new();
///
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push("x", 42_i64);
///
/// // Notice that a PathBuf is required which can easily be constructed from a string.
/// let result = engine.eval_file_with_scope::<i64>(&mut scope, "script.rhai".into())?;
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_std"))]
pub fn eval_file_with_scope<T: Any + Clone>(
&mut self,
scope: &mut Scope,
path: PathBuf,
) -> Result<T, EvalAltResult> {
Self::read_file(path).and_then(|contents| self.eval_with_scope::<T>(scope, &contents))
}
/// Evaluate a string.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
/// assert_eq!(engine.eval::<i64>("40 + 2")?, 42);
/// # Ok(())
/// # }
/// ```
pub fn eval<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
2020-03-30 16:19:37 +02:00
self.eval_with_scope(&mut Scope::new(), input)
}
/// Evaluate a string with own scope.
2020-03-19 06:52:10 +01:00
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope};
///
/// let mut engine = Engine::new();
///
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push("x", 40_i64);
///
/// assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x = x + 2; x")?, 42);
/// assert_eq!(engine.eval_with_scope::<i64>(&mut scope, "x = x + 2; x")?, 44);
///
/// // The variable in the scope is modified
/// assert_eq!(scope.get_value::<i64>("x").expect("variable x should exist"), 44);
/// # Ok(())
/// # }
/// ```
pub fn eval_with_scope<T: Any + Clone>(
&mut self,
scope: &mut Scope,
input: &str,
) -> Result<T, EvalAltResult> {
2020-03-09 14:57:07 +01:00
let ast = self.compile(input).map_err(EvalAltResult::ErrorParsing)?;
self.eval_ast_with_scope(scope, &ast)
}
2020-03-22 14:03:58 +01:00
/// Evaluate a string containing an expression.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
/// assert_eq!(engine.eval_expression::<i64>("40 + 2")?, 42);
/// # Ok(())
/// # }
/// ```
pub fn eval_expression<T: Any + Clone>(&mut self, input: &str) -> Result<T, EvalAltResult> {
2020-03-30 16:19:37 +02:00
self.eval_expression_with_scope(&mut Scope::new(), input)
2020-03-22 14:03:58 +01:00
}
/// Evaluate a string containing an expression with own scope.
///
/// # Example
///
/// ```
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope};
///
/// let mut engine = Engine::new();
///
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push("x", 40_i64);
///
/// assert_eq!(engine.eval_expression_with_scope::<i64>(&mut scope, "x + 2")?, 42);
/// # Ok(())
/// # }
/// ```
pub fn eval_expression_with_scope<T: Any + Clone>(
&mut self,
scope: &mut Scope,
input: &str,
) -> Result<T, EvalAltResult> {
let ast = self
.compile_expression(input)
.map_err(EvalAltResult::ErrorParsing)?;
self.eval_ast_with_scope(scope, &ast)
}
2020-03-19 06:52:10 +01:00
/// Evaluate an `AST`.
///
/// # Example
///
2020-03-19 12:53:42 +01:00
/// ```
2020-03-19 06:52:10 +01:00
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
/// let mut engine = Engine::new();
///
2020-03-19 12:53:42 +01:00
/// // Compile a script to an AST and store it for later evaluation
2020-03-19 06:52:10 +01:00
/// let ast = engine.compile("40 + 2")?;
///
/// // Evaluate it
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
/// # Ok(())
/// # }
/// ```
pub fn eval_ast<T: Any + Clone>(&mut self, ast: &AST) -> Result<T, EvalAltResult> {
2020-03-30 16:19:37 +02:00
self.eval_ast_with_scope(&mut Scope::new(), ast)
}
2020-03-19 06:52:10 +01:00
/// Evaluate an `AST` with own scope.
///
/// # Example
///
2020-03-19 12:53:42 +01:00
/// ```
2020-03-19 06:52:10 +01:00
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::{Engine, Scope};
///
/// let mut engine = Engine::new();
///
2020-03-19 12:53:42 +01:00
/// // Compile a script to an AST and store it for later evaluation
2020-03-19 06:52:10 +01:00
/// let ast = engine.compile("x + 2")?;
///
/// // Create initialized scope
/// let mut scope = Scope::new();
/// scope.push("x", 40_i64);
///
2020-03-19 12:53:42 +01:00
/// // Compile a script to an AST and store it for later evaluation
/// let ast = engine.compile("x = x + 2; x")?;
///
2020-03-19 06:52:10 +01:00
/// // Evaluate it
2020-03-19 12:53:42 +01:00
/// assert_eq!(engine.eval_ast_with_scope::<i64>(&mut scope, &ast)?, 42);
/// assert_eq!(engine.eval_ast_with_scope::<i64>(&mut scope, &ast)?, 44);
2020-03-19 06:52:10 +01:00
///
/// // The variable in the scope is modified
/// assert_eq!(scope.get_value::<i64>("x").expect("variable x should exist"), 44);
/// # Ok(())
/// # }
/// ```
pub fn eval_ast_with_scope<T: Any + Clone>(
&mut self,
scope: &mut Scope,
ast: &AST,
) -> Result<T, EvalAltResult> {
2020-04-01 03:51:33 +02:00
self.eval_ast_with_scope_raw(scope, false, ast)?
.downcast::<T>()
.map(|v| *v)
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).to_string(),
Position::none(),
)
2020-03-19 12:53:42 +01:00
})
}
pub(crate) fn eval_ast_with_scope_raw(
&mut self,
scope: &mut Scope,
retain_functions: bool,
ast: &AST,
) -> Result<Dynamic, EvalAltResult> {
2020-03-30 16:19:37 +02:00
if !retain_functions {
self.clear_functions();
}
let statements = {
let AST(statements, functions) = ast;
self.load_script_functions(functions);
statements
};
let result = statements
.iter()
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0));
if !retain_functions {
self.clear_functions();
}
2020-03-30 16:19:37 +02:00
result.or_else(|err| match err {
2020-03-19 12:53:42 +01:00
EvalAltResult::Return(out, _) => Ok(out),
_ => Err(err),
})
}
2020-03-04 15:00:01 +01:00
/// Evaluate a file, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
2020-04-01 03:51:33 +02:00
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run.
#[cfg(not(feature = "no_std"))]
pub fn consume_file(
&mut self,
retain_functions: bool,
2020-03-13 11:27:53 +01:00
path: PathBuf,
) -> Result<(), EvalAltResult> {
Self::read_file(path).and_then(|contents| self.consume(retain_functions, &contents))
}
/// Evaluate a file with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
2020-04-01 03:51:33 +02:00
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_ and not cleared from run to run.
#[cfg(not(feature = "no_std"))]
pub fn consume_file_with_scope(
&mut self,
scope: &mut Scope,
retain_functions: bool,
path: PathBuf,
) -> Result<(), EvalAltResult> {
Self::read_file(path)
.and_then(|contents| self.consume_with_scope(scope, retain_functions, &contents))
}
2020-03-04 15:00:01 +01:00
/// Evaluate a string, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
2020-04-01 03:51:33 +02:00
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume(&mut self, retain_functions: bool, input: &str) -> Result<(), EvalAltResult> {
self.consume_with_scope(&mut Scope::new(), retain_functions, input)
}
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
2020-04-01 03:51:33 +02:00
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume_with_scope(
&mut self,
scope: &mut Scope,
retain_functions: bool,
input: &str,
) -> Result<(), EvalAltResult> {
let tokens_stream = lex(input);
let ast = parse(&mut tokens_stream.peekable(), self, scope)
.map_err(EvalAltResult::ErrorParsing)?;
self.consume_ast_with_scope(scope, retain_functions, &ast)
}
/// Evaluate an AST, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
2020-04-01 03:51:33 +02:00
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume_ast(&mut self, retain_functions: bool, ast: &AST) -> Result<(), EvalAltResult> {
self.consume_ast_with_scope(&mut Scope::new(), retain_functions, ast)
}
2020-03-19 06:52:10 +01:00
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
/// Useful for when you don't need the result, but still need to keep track of possible errors.
///
2020-04-01 03:51:33 +02:00
/// # Note
///
/// If `retain_functions` is set to `true`, functions defined by previous scripts are _retained_and not cleared from run to run.
pub fn consume_ast_with_scope(
&mut self,
scope: &mut Scope,
retain_functions: bool,
ast: &AST,
) -> Result<(), EvalAltResult> {
if !retain_functions {
self.clear_functions();
}
let statements = {
let AST(ref statements, ref functions) = ast;
self.load_script_functions(functions);
statements
};
let result = statements
.iter()
2020-03-30 16:19:37 +02:00
.try_fold(().into_dynamic(), |_, stmt| self.eval_stmt(scope, stmt, 0));
if !retain_functions {
self.clear_functions();
}
2020-03-30 16:19:37 +02:00
result.map(|_| ()).or_else(|err| match err {
2020-03-17 09:52:06 +01:00
EvalAltResult::Return(_, _) => Ok(()),
_ => Err(err),
})
}
/// Load a list of functions into the Engine.
pub(crate) fn load_script_functions<'a>(
&mut self,
functions: impl IntoIterator<Item = &'a Arc<FnDef>>,
) {
2020-04-01 03:51:33 +02:00
functions.into_iter().cloned().for_each(|f| {
self.fn_lib.add_or_replace_function(f);
});
}
/// Call a script function retained inside the Engine.
2020-03-04 15:00:01 +01:00
///
/// # Example
///
2020-03-19 06:52:10 +01:00
/// ```
2020-03-09 14:57:07 +01:00
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// # #[cfg(not(feature = "no_stdlib"))]
/// # #[cfg(not(feature = "no_function"))]
/// # {
2020-03-09 14:57:07 +01:00
/// use rhai::Engine;
///
2020-03-04 15:00:01 +01:00
/// let mut engine = Engine::new();
///
2020-03-19 06:52:10 +01:00
/// // Set 'retain_functions' in 'consume' to keep the function definitions
/// engine.consume(true, "fn add(x, y) { len(x) + y }")?;
2020-03-04 15:00:01 +01:00
///
2020-03-19 06:52:10 +01:00
/// // Call the script-defined function
/// let result: i64 = engine.call_fn("add", (String::from("abc"), 123_i64))?;
2020-03-04 15:00:01 +01:00
///
/// assert_eq!(result, 126);
/// # }
2020-03-04 15:00:01 +01:00
/// # Ok(())
/// # }
/// ```
#[cfg(not(feature = "no_function"))]
pub fn call_fn<A: FuncArgs, T: Any + Clone>(
2020-03-04 15:00:01 +01:00
&mut self,
name: &str,
args: A,
) -> Result<T, EvalAltResult> {
2020-03-27 09:46:19 +01:00
let mut values = args.into_vec();
let mut arg_values: Vec<_> = values.iter_mut().map(Dynamic::as_mut).collect();
2020-04-01 03:51:33 +02:00
self.call_fn_raw(name, &mut arg_values, None, Position::none(), 0)?
.downcast()
.map(|b| *b)
.map_err(|a| {
EvalAltResult::ErrorMismatchOutputType(
self.map_type_name((*a).type_name()).into(),
Position::none(),
)
})
2020-03-04 15:00:01 +01:00
}
2020-03-19 06:52:10 +01:00
/// Optimize the `AST` with constants defined in an external Scope.
/// An optimized copy of the `AST` is returned while the original `AST` is untouched.
///
/// Although optimization is performed by default during compilation, sometimes it is necessary to
/// _re_-optimize an AST. For example, when working with constants that are passed in via an
2020-03-19 06:52:10 +01:00
/// external scope, it will be more efficient to optimize the `AST` once again to take advantage
/// of the new constants.
///
2020-03-19 06:52:10 +01:00
/// With this method, it is no longer necessary to recompile a large script. The script `AST` can be
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
2020-03-19 06:52:10 +01:00
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
#[cfg(not(feature = "no_optimize"))]
pub fn optimize_ast(&self, scope: &Scope, ast: &AST) -> AST {
2020-03-18 11:41:18 +01:00
let statements = ast.0.clone();
let functions = ast.1.iter().map(|f| (**f).clone()).collect();
optimize_into_ast(self, scope, statements, functions)
}
2020-03-04 15:00:01 +01:00
/// Override default action of `print` (print to stdout using `println!`)
///
/// # Example
///
2020-03-19 06:52:10 +01:00
/// ```
2020-03-09 14:57:07 +01:00
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
2020-03-04 15:00:01 +01:00
/// let mut result = String::from("");
/// {
2020-03-19 06:52:10 +01:00
/// let mut engine = Engine::new();
2020-03-04 15:00:01 +01:00
///
2020-03-19 06:52:10 +01:00
/// // Override action of 'print' function
/// engine.on_print(|s| result.push_str(s));
/// engine.consume(false, "print(40 + 2);")?;
2020-03-04 15:00:01 +01:00
/// }
/// assert_eq!(result, "42");
2020-03-09 14:57:07 +01:00
/// # Ok(())
/// # }
2020-03-04 15:00:01 +01:00
/// ```
pub fn on_print(&mut self, callback: impl FnMut(&str) + 'e) {
self.on_print = Box::new(callback);
}
2020-03-04 15:00:01 +01:00
/// Override default action of `debug` (print to stdout using `println!`)
///
/// # Example
///
2020-03-19 06:52:10 +01:00
/// ```
2020-03-09 14:57:07 +01:00
/// # fn main() -> Result<(), rhai::EvalAltResult> {
/// use rhai::Engine;
///
2020-03-04 15:00:01 +01:00
/// let mut result = String::from("");
/// {
2020-03-19 06:52:10 +01:00
/// let mut engine = Engine::new();
2020-03-04 15:00:01 +01:00
///
2020-03-19 06:52:10 +01:00
/// // Override action of 'debug' function
/// engine.on_debug(|s| result.push_str(s));
/// engine.consume(false, r#"debug("hello");"#)?;
2020-03-04 15:00:01 +01:00
/// }
/// assert_eq!(result, "\"hello\"");
2020-03-09 14:57:07 +01:00
/// # Ok(())
/// # }
2020-03-04 15:00:01 +01:00
/// ```
pub fn on_debug(&mut self, callback: impl FnMut(&str) + 'e) {
self.on_debug = Box::new(callback);
}
}