2020-03-08 19:54:02 +08:00
|
|
|
//! Module that defines the extern API of `Engine`.
|
|
|
|
|
2020-04-12 23:00:06 +08:00
|
|
|
use crate::any::{Dynamic, Variant};
|
2020-05-06 19:45:17 +08:00
|
|
|
use crate::engine::{make_getter, make_setter, Engine, State, FUNC_INDEXER};
|
2020-03-04 22:00:01 +08:00
|
|
|
use crate::error::ParseError;
|
2020-04-08 23:01:48 +08:00
|
|
|
use crate::fn_call::FuncArgs;
|
2020-03-04 22:00:01 +08:00
|
|
|
use crate::fn_register::RegisterFn;
|
2020-04-10 12:16:39 +08:00
|
|
|
use crate::optimize::{optimize_into_ast, OptimizationLevel};
|
2020-04-15 22:21:23 +08:00
|
|
|
use crate::parser::{parse, parse_global_expr, AST};
|
2020-03-04 22:00:01 +08:00
|
|
|
use crate::result::EvalAltResult;
|
2020-03-03 15:20:20 +08:00
|
|
|
use crate::scope::Scope;
|
2020-04-15 22:21:23 +08:00
|
|
|
use crate::token::{lex, Position};
|
2020-05-10 21:25:47 +08:00
|
|
|
use crate::utils::StaticVec;
|
2020-03-15 22:39:58 +08:00
|
|
|
|
2020-05-06 19:45:17 +08:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
use crate::engine::Map;
|
|
|
|
|
2020-03-17 19:26:11 +01:00
|
|
|
use crate::stdlib::{
|
2020-03-10 10:07:44 +08:00
|
|
|
any::{type_name, TypeId},
|
2020-03-17 19:26:11 +01:00
|
|
|
boxed::Box,
|
2020-04-04 13:05:20 +08:00
|
|
|
collections::HashMap,
|
2020-05-05 15:00:10 +08:00
|
|
|
mem,
|
2020-03-17 19:26:11 +01:00
|
|
|
string::{String, ToString},
|
2020-03-10 10:07:44 +08:00
|
|
|
};
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-03-17 19:26:11 +01:00
|
|
|
use crate::stdlib::{fs::File, io::prelude::*, path::PathBuf};
|
2020-03-02 14:28:42 +08:00
|
|
|
|
2020-04-03 17:17:00 +08:00
|
|
|
// Define callback function types
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
pub trait ObjectGetCallback<T, U>: Fn(&mut T) -> U + Send + Sync + 'static {}
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
impl<F: Fn(&mut T) -> U + Send + Sync + 'static, T, U> ObjectGetCallback<T, U> for F {}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
pub trait ObjectGetCallback<T, U>: Fn(&mut T) -> U + 'static {}
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
impl<F: Fn(&mut T) -> U + 'static, T, U> ObjectGetCallback<T, U> for F {}
|
|
|
|
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
pub trait ObjectSetCallback<T, U>: Fn(&mut T, U) + Send + Sync + 'static {}
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
impl<F: Fn(&mut T, U) + Send + Sync + 'static, T, U> ObjectSetCallback<T, U> for F {}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
pub trait ObjectSetCallback<T, U>: Fn(&mut T, U) + 'static {}
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
impl<F: Fn(&mut T, U) + 'static, T, U> ObjectSetCallback<T, U> for F {}
|
|
|
|
|
2020-05-05 20:38:48 +08:00
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
pub trait ObjectIndexerCallback<T, X, U>: Fn(&mut T, X) -> U + Send + Sync + 'static {}
|
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
impl<F: Fn(&mut T, X) -> U + Send + Sync + 'static, T, X, U> ObjectIndexerCallback<T, X, U> for F {}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
pub trait ObjectIndexerCallback<T, X, U>: Fn(&mut T, X) -> U + 'static {}
|
|
|
|
#[cfg(not(feature = "sync"))]
|
|
|
|
impl<F: Fn(&mut T, X) -> U + 'static, T, X, U> ObjectIndexerCallback<T, X, U> for F {}
|
|
|
|
|
2020-04-03 17:17:00 +08:00
|
|
|
#[cfg(feature = "sync")]
|
|
|
|
pub trait IteratorCallback:
|
2020-04-24 22:54:56 +08:00
|
|
|
Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync + 'static
|
2020-04-03 17:17:00 +08:00
|
|
|
{
|
|
|
|
}
|
|
|
|
#[cfg(feature = "sync")]
|
2020-04-24 22:54:56 +08:00
|
|
|
impl<F: Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send + Sync + 'static> IteratorCallback
|
2020-04-03 17:17:00 +08:00
|
|
|
for F
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
#[cfg(not(feature = "sync"))]
|
2020-04-24 22:54:56 +08:00
|
|
|
pub trait IteratorCallback: Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static {}
|
2020-04-03 17:17:00 +08:00
|
|
|
#[cfg(not(feature = "sync"))]
|
2020-04-24 22:54:56 +08:00
|
|
|
impl<F: Fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + 'static> IteratorCallback for F {}
|
2020-04-03 17:17:00 +08:00
|
|
|
|
|
|
|
/// Engine public API
|
2020-04-16 23:31:48 +08:00
|
|
|
impl Engine {
|
2020-03-04 22:00:01 +08:00
|
|
|
/// Register a custom type for use with the `Engine`.
|
2020-03-19 13:52:10 +08:00
|
|
|
/// The type must implement `Clone`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-03-30 16:10:50 +08:00
|
|
|
/// #[derive(Debug, Clone, Eq, PartialEq)]
|
2020-03-19 13:52:10 +08:00
|
|
|
/// struct TestStruct {
|
|
|
|
/// field: i64
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl TestStruct {
|
2020-03-22 10:18:16 +08:00
|
|
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
|
|
|
/// fn update(&mut self, offset: i64) { self.field += offset; }
|
2020-03-19 13:52:10 +08:00
|
|
|
/// }
|
|
|
|
///
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// 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 10:18:16 +08:00
|
|
|
/// // Use `register_fn` to register methods on the type.
|
2020-03-19 13:52:10 +08:00
|
|
|
/// engine.register_fn("update", TestStruct::update);
|
|
|
|
///
|
|
|
|
/// assert_eq!(
|
2020-03-30 16:10:50 +08:00
|
|
|
/// engine.eval::<TestStruct>("let x = new_ts(); x.update(41); x")?,
|
|
|
|
/// TestStruct { field: 42 }
|
2020-03-19 13:52:10 +08:00
|
|
|
/// );
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-29 17:15:12 +08:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn register_type<T: Variant + Clone>(&mut self) {
|
2020-03-08 19:54:02 +08:00
|
|
|
self.register_type_with_name::<T>(type_name::<T>());
|
2020-03-04 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08: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 16:10:50 +08:00
|
|
|
/// struct TestStruct {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// field: i64
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl TestStruct {
|
|
|
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
|
|
|
/// }
|
|
|
|
///
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-29 17:15:12 +08:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) {
|
2020-03-04 22:00:01 +08:00
|
|
|
// Add the pretty-print type name into the map
|
2020-03-08 19:54:02 +08:00
|
|
|
self.type_names
|
|
|
|
.insert(type_name::<T>().to_string(), name.to_string());
|
2020-03-04 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register an iterator adapter for a type with the `Engine`.
|
2020-03-19 13:52:10 +08:00
|
|
|
/// This is an advanced feature.
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn register_iterator<T: Variant + Clone, F: IteratorCallback>(&mut self, f: F) {
|
2020-05-07 15:25:50 +08:00
|
|
|
self.base_package
|
|
|
|
.type_iterators
|
|
|
|
.insert(TypeId::of::<T>(), Box::new(f));
|
2020-03-04 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a getter function for a member of a registered type with the `Engine`.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
2020-03-22 10:18:16 +08:00
|
|
|
/// The function signature must start with `&mut self` and not `&self`.
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// #[derive(Clone)]
|
2020-03-30 16:10:50 +08:00
|
|
|
/// struct TestStruct {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// field: i64
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl TestStruct {
|
|
|
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
2020-03-22 10:18:16 +08:00
|
|
|
///
|
|
|
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
2020-03-19 13:52:10 +08:00
|
|
|
/// fn get_field(&mut self) -> i64 { self.field }
|
|
|
|
/// }
|
|
|
|
///
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-29 17:15:12 +08:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-03 17:17:00 +08:00
|
|
|
pub fn register_get<T, U, F>(&mut self, name: &str, callback: F)
|
|
|
|
where
|
2020-04-12 23:00:06 +08:00
|
|
|
T: Variant + Clone,
|
|
|
|
U: Variant + Clone,
|
2020-04-03 17:17:00 +08:00
|
|
|
F: ObjectGetCallback<T, U>,
|
|
|
|
{
|
2020-03-30 22:19:37 +08:00
|
|
|
self.register_fn(&make_getter(name), callback);
|
2020-03-04 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Register a setter function for a member of a registered type with the `Engine`.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-03-30 16:10:50 +08:00
|
|
|
/// #[derive(Debug, Clone, Eq, PartialEq)]
|
2020-03-19 13:52:10 +08: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; }
|
|
|
|
/// }
|
|
|
|
///
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// 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 16:10:50 +08:00
|
|
|
/// assert_eq!(
|
|
|
|
/// engine.eval::<TestStruct>("let a = new_ts(); a.xyz = 42; a")?,
|
|
|
|
/// TestStruct { field: 42 }
|
|
|
|
/// );
|
2020-03-19 13:52:10 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-29 17:15:12 +08:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-03 17:17:00 +08:00
|
|
|
pub fn register_set<T, U, F>(&mut self, name: &str, callback: F)
|
|
|
|
where
|
2020-04-12 23:00:06 +08:00
|
|
|
T: Variant + Clone,
|
|
|
|
U: Variant + Clone,
|
2020-04-03 17:17:00 +08:00
|
|
|
F: ObjectSetCallback<T, U>,
|
|
|
|
{
|
2020-03-30 22:19:37 +08:00
|
|
|
self.register_fn(&make_setter(name), callback);
|
2020-03-04 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Shorthand for registering both getter and setter functions
|
|
|
|
/// of a registered type with the `Engine`.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
2020-03-22 10:18:16 +08:00
|
|
|
/// All function signatures must start with `&mut self` and not `&self`.
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// #[derive(Clone)]
|
|
|
|
/// struct TestStruct {
|
|
|
|
/// field: i64
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl TestStruct {
|
2020-03-22 10:18:16 +08:00
|
|
|
/// fn new() -> Self { TestStruct { field: 1 } }
|
2020-03-19 13:52:10 +08:00
|
|
|
/// fn get_field(&mut self) -> i64 { self.field }
|
2020-03-22 10:18:16 +08:00
|
|
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
2020-03-19 13:52:10 +08:00
|
|
|
/// fn set_field(&mut self, new_val: i64) { self.field = new_val; }
|
|
|
|
/// }
|
|
|
|
///
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-29 17:15:12 +08:00
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-03 17:17:00 +08:00
|
|
|
pub fn register_get_set<T, U, G, S>(&mut self, name: &str, get_fn: G, set_fn: S)
|
|
|
|
where
|
2020-04-12 23:00:06 +08:00
|
|
|
T: Variant + Clone,
|
|
|
|
U: Variant + Clone,
|
2020-04-03 17:17:00 +08:00
|
|
|
G: ObjectGetCallback<T, U>,
|
|
|
|
S: ObjectSetCallback<T, U>,
|
|
|
|
{
|
2020-03-04 22:00:01 +08:00
|
|
|
self.register_get(name, get_fn);
|
|
|
|
self.register_set(name, set_fn);
|
|
|
|
}
|
|
|
|
|
2020-05-05 20:38:48 +08:00
|
|
|
/// Register an indexer function for a registered type with the `Engine`.
|
|
|
|
///
|
|
|
|
/// The function signature must start with `&mut self` and not `&self`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
|
|
|
/// #[derive(Clone)]
|
|
|
|
/// struct TestStruct {
|
|
|
|
/// fields: Vec<i64>
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// impl TestStruct {
|
|
|
|
/// fn new() -> Self { TestStruct { fields: vec![1, 2, 3, 4, 5] } }
|
|
|
|
///
|
|
|
|
/// // Even a getter must start with `&mut self` and not `&self`.
|
|
|
|
/// fn get_field(&mut self, index: i64) -> i64 { self.fields[index as usize] }
|
|
|
|
/// }
|
|
|
|
///
|
|
|
|
/// # fn main() -> Result<(), Box<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 an indexer.
|
|
|
|
/// engine.register_indexer(TestStruct::get_field);
|
|
|
|
///
|
|
|
|
/// assert_eq!(engine.eval::<i64>("let a = new_ts(); a[2]")?, 3);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
|
|
|
#[cfg(not(feature = "no_index"))]
|
|
|
|
pub fn register_indexer<T, X, U, F>(&mut self, callback: F)
|
|
|
|
where
|
|
|
|
T: Variant + Clone,
|
|
|
|
U: Variant + Clone,
|
|
|
|
X: Variant + Clone,
|
|
|
|
F: ObjectIndexerCallback<T, X, U>,
|
|
|
|
{
|
|
|
|
self.register_fn(FUNC_INDEXER, callback);
|
|
|
|
}
|
|
|
|
|
2020-03-19 19:53:42 +08:00
|
|
|
/// Compile a string into an `AST`, which can be used later for evaluation.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Compile a script to an AST and store it for later evaluation
|
2020-03-19 13:52:10 +08:00
|
|
|
/// let ast = engine.compile("40 + 2")?;
|
|
|
|
///
|
|
|
|
/// for _ in 0..42 {
|
|
|
|
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
|
|
|
|
/// }
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn compile(&self, script: &str) -> Result<AST, Box<ParseError>> {
|
2020-04-08 16:57:15 +08:00
|
|
|
self.compile_with_scope(&Scope::new(), script)
|
2020-03-14 14:30:44 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 19:53:42 +08: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 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 19:53:42 +08:00
|
|
|
/// # #[cfg(not(feature = "no_optimize"))]
|
|
|
|
/// # {
|
|
|
|
/// use rhai::{Engine, Scope, OptimizationLevel};
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
2020-03-19 19:53:42 +08: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 13:52:10 +08:00
|
|
|
/// // Create initialized scope
|
|
|
|
/// let mut scope = Scope::new();
|
|
|
|
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
|
|
|
///
|
2020-03-19 19:53:42 +08: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 13:52:10 +08:00
|
|
|
/// let ast = engine.compile_with_scope(&mut scope,
|
2020-03-19 19:53:42 +08:00
|
|
|
/// "if x > 40 { x } else { 0 }" // all 'x' are replaced with 42
|
2020-03-19 13:52:10 +08:00
|
|
|
/// )?;
|
|
|
|
///
|
2020-03-19 19:53:42 +08: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 13:52:10 +08:00
|
|
|
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
|
2020-03-19 19:53:42 +08:00
|
|
|
/// # }
|
2020-03-19 13:52:10 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn compile_with_scope(&self, scope: &Scope, script: &str) -> Result<AST, Box<ParseError>> {
|
2020-04-10 12:16:39 +08:00
|
|
|
self.compile_with_scope_and_optimization_level(scope, script, self.optimization_level)
|
2020-04-08 10:19:03 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Compile a string into an `AST` using own scope at a specific optimization level.
|
|
|
|
pub(crate) fn compile_with_scope_and_optimization_level(
|
|
|
|
&self,
|
|
|
|
scope: &Scope,
|
2020-04-08 16:57:15 +08:00
|
|
|
script: &str,
|
2020-04-10 12:16:39 +08:00
|
|
|
optimization_level: OptimizationLevel,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<AST, Box<ParseError>> {
|
2020-04-10 17:14:07 +08:00
|
|
|
let scripts = [script];
|
|
|
|
let stream = lex(&scripts);
|
2020-04-21 23:25:12 +08:00
|
|
|
parse(&mut stream.peekable(), self, scope, optimization_level)
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Read the contents of a file into a string.
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-04-21 23:25:12 +08:00
|
|
|
fn read_file(path: PathBuf) -> Result<String, Box<EvalAltResult>> {
|
2020-05-06 00:09:04 +08:00
|
|
|
let mut f = File::open(path.clone()).map_err(|err| {
|
|
|
|
Box::new(EvalAltResult::ErrorReadingScriptFile(
|
|
|
|
path.clone(),
|
|
|
|
Position::none(),
|
|
|
|
err,
|
|
|
|
))
|
|
|
|
})?;
|
2020-03-02 14:28:42 +08:00
|
|
|
|
|
|
|
let mut contents = String::new();
|
|
|
|
|
2020-05-06 00:09:04 +08:00
|
|
|
f.read_to_string(&mut contents).map_err(|err| {
|
|
|
|
Box::new(EvalAltResult::ErrorReadingScriptFile(
|
|
|
|
path.clone(),
|
|
|
|
Position::none(),
|
|
|
|
err,
|
|
|
|
))
|
|
|
|
})?;
|
2020-03-30 22:19:37 +08:00
|
|
|
|
|
|
|
Ok(contents)
|
2020-03-10 17:10:33 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 19:53:42 +08:00
|
|
|
/// Compile a script file into an `AST`, which can be used later for evaluation.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```no_run
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Compile a script file to an AST and store it for later evaluation.
|
2020-03-19 13:52:10 +08: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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn compile_file(&self, path: PathBuf) -> Result<AST, Box<EvalAltResult>> {
|
2020-03-14 14:33:56 +08:00
|
|
|
self.compile_file_with_scope(&Scope::new(), path)
|
2020-03-14 14:30:44 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 19:53:42 +08: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 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```no_run
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 19:53:42 +08:00
|
|
|
/// # #[cfg(not(feature = "no_optimize"))]
|
|
|
|
/// # {
|
|
|
|
/// use rhai::{Engine, Scope, OptimizationLevel};
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Set optimization level to 'Full' so the Engine can fold constants.
|
|
|
|
/// engine.set_optimization_level(OptimizationLevel::Full);
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// // Create initialized scope
|
|
|
|
/// let mut scope = Scope::new();
|
|
|
|
/// scope.push_constant("x", 42_i64); // 'x' is a constant
|
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Compile a script to an AST and store it for later evaluation.
|
2020-03-19 13:52:10 +08: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 19:53:42 +08:00
|
|
|
/// # }
|
2020-03-19 13:52:10 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-03-14 14:30:44 +08:00
|
|
|
pub fn compile_file_with_scope(
|
|
|
|
&self,
|
|
|
|
scope: &Scope,
|
2020-03-14 14:33:56 +08:00
|
|
|
path: PathBuf,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<AST, Box<EvalAltResult>> {
|
2020-04-08 16:57:15 +08:00
|
|
|
Self::read_file(path).and_then(|contents| Ok(self.compile_with_scope(scope, &contents)?))
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-04-10 17:14:07 +08:00
|
|
|
/// Parse a JSON string into a map.
|
|
|
|
///
|
|
|
|
/// Set `has_null` to `true` in order to map `null` values to `()`.
|
|
|
|
/// Setting it to `false` will cause a _variable not found_ error during parsing.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-04-12 23:00:06 +08:00
|
|
|
/// use rhai::Engine;
|
2020-04-10 17:14:07 +08:00
|
|
|
///
|
|
|
|
/// let engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// let map = engine.parse_json(r#"{"a":123, "b":42, "c":false, "d":null}"#, true)?;
|
|
|
|
///
|
|
|
|
/// assert_eq!(map.len(), 4);
|
|
|
|
/// assert_eq!(map.get("a").cloned().unwrap().cast::<i64>(), 123);
|
|
|
|
/// assert_eq!(map.get("b").cloned().unwrap().cast::<i64>(), 42);
|
|
|
|
/// assert_eq!(map.get("c").cloned().unwrap().cast::<bool>(), false);
|
|
|
|
/// assert_eq!(map.get("d").cloned().unwrap().cast::<()>(), ());
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
|
|
|
#[cfg(not(feature = "no_object"))]
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn parse_json(&self, json: &str, has_null: bool) -> Result<Map, Box<EvalAltResult>> {
|
2020-04-10 17:14:07 +08:00
|
|
|
let mut scope = Scope::new();
|
|
|
|
|
|
|
|
// Trims the JSON string and add a '#' in front
|
|
|
|
let scripts = ["#", json.trim()];
|
|
|
|
let stream = lex(&scripts);
|
|
|
|
let ast = parse_global_expr(
|
|
|
|
&mut stream.peekable(),
|
|
|
|
self,
|
|
|
|
&scope,
|
|
|
|
OptimizationLevel::None,
|
|
|
|
)?;
|
|
|
|
|
|
|
|
// Handle null - map to ()
|
|
|
|
if has_null {
|
|
|
|
scope.push_constant("null", ());
|
|
|
|
}
|
|
|
|
|
|
|
|
self.eval_ast_with_scope(&mut scope, &ast)
|
|
|
|
}
|
|
|
|
|
2020-03-22 21:03:58 +08:00
|
|
|
/// Compile a string containing an expression into an `AST`,
|
|
|
|
/// which can be used later for evaluation.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-22 21:03:58 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-22 21:03:58 +08:00
|
|
|
///
|
|
|
|
/// // 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn compile_expression(&self, script: &str) -> Result<AST, Box<ParseError>> {
|
2020-04-08 16:57:15 +08:00
|
|
|
self.compile_expression_with_scope(&Scope::new(), script)
|
2020-03-22 21:03:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-22 21:03:58 +08:00
|
|
|
/// # #[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,
|
2020-04-08 16:57:15 +08:00
|
|
|
script: &str,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<AST, Box<ParseError>> {
|
2020-04-10 17:14:07 +08:00
|
|
|
let scripts = [script];
|
|
|
|
let stream = lex(&scripts);
|
2020-04-18 00:14:33 +08:00
|
|
|
|
2020-04-10 17:14:07 +08:00
|
|
|
parse_global_expr(&mut stream.peekable(), self, scope, self.optimization_level)
|
2020-03-22 21:03:58 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Evaluate a script file.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```no_run
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// // Notice that a PathBuf is required which can easily be constructed from a string.
|
|
|
|
/// let result = engine.eval_file::<i64>("script.rhai".into())?;
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn eval_file<T: Variant + Clone>(&self, path: PathBuf) -> Result<T, Box<EvalAltResult>> {
|
2020-03-13 18:27:53 +08:00
|
|
|
Self::read_file(path).and_then(|contents| self.eval::<T>(&contents))
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Evaluate a script file with own scope.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```no_run
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::{Engine, Scope};
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// // 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn eval_file_with_scope<T: Variant + Clone>(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-15 22:39:58 +08:00
|
|
|
scope: &mut Scope,
|
|
|
|
path: PathBuf,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<T, Box<EvalAltResult>> {
|
2020-03-15 22:39:58 +08:00
|
|
|
Self::read_file(path).and_then(|contents| self.eval_with_scope::<T>(scope, &contents))
|
|
|
|
}
|
|
|
|
|
2020-03-10 09:30:12 +08:00
|
|
|
/// Evaluate a string.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// assert_eq!(engine.eval::<i64>("40 + 2")?, 42);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn eval<T: Variant + Clone>(&self, script: &str) -> Result<T, Box<EvalAltResult>> {
|
2020-04-08 16:57:15 +08:00
|
|
|
self.eval_with_scope(&mut Scope::new(), script)
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-10 09:30:12 +08:00
|
|
|
/// Evaluate a string with own scope.
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::{Engine, Scope};
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// // 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn eval_with_scope<T: Variant + Clone>(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-02 14:28:42 +08:00
|
|
|
scope: &mut Scope,
|
2020-04-08 16:57:15 +08:00
|
|
|
script: &str,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<T, Box<EvalAltResult>> {
|
2020-04-10 21:02:38 +08:00
|
|
|
// Since the AST will be thrown away afterwards, don't bother to optimize it
|
|
|
|
let ast =
|
|
|
|
self.compile_with_scope_and_optimization_level(scope, script, OptimizationLevel::None)?;
|
2020-03-12 14:54:14 +08:00
|
|
|
self.eval_ast_with_scope(scope, &ast)
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-22 21:03:58 +08:00
|
|
|
/// Evaluate a string containing an expression.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-22 21:03:58 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-22 21:03:58 +08:00
|
|
|
///
|
|
|
|
/// assert_eq!(engine.eval_expression::<i64>("40 + 2")?, 42);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn eval_expression<T: Variant + Clone>(
|
|
|
|
&self,
|
|
|
|
script: &str,
|
|
|
|
) -> Result<T, Box<EvalAltResult>> {
|
2020-04-08 16:57:15 +08:00
|
|
|
self.eval_expression_with_scope(&mut Scope::new(), script)
|
2020-03-22 21:03:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// Evaluate a string containing an expression with own scope.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-22 21:03:58 +08:00
|
|
|
/// use rhai::{Engine, Scope};
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-22 21:03:58 +08:00
|
|
|
///
|
|
|
|
/// // 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(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn eval_expression_with_scope<T: Variant + Clone>(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-22 21:03:58 +08:00
|
|
|
scope: &mut Scope,
|
2020-04-08 16:57:15 +08:00
|
|
|
script: &str,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<T, Box<EvalAltResult>> {
|
2020-04-10 21:02:38 +08:00
|
|
|
let scripts = [script];
|
|
|
|
let stream = lex(&scripts);
|
|
|
|
// Since the AST will be thrown away afterwards, don't bother to optimize it
|
|
|
|
let ast = parse_global_expr(&mut stream.peekable(), self, scope, OptimizationLevel::None)?;
|
2020-03-22 21:03:58 +08:00
|
|
|
self.eval_ast_with_scope(scope, &ast)
|
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Evaluate an `AST`.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Compile a script to an AST and store it for later evaluation
|
2020-03-19 13:52:10 +08:00
|
|
|
/// let ast = engine.compile("40 + 2")?;
|
|
|
|
///
|
|
|
|
/// // Evaluate it
|
|
|
|
/// assert_eq!(engine.eval_ast::<i64>(&ast)?, 42);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn eval_ast<T: Variant + Clone>(&self, ast: &AST) -> Result<T, Box<EvalAltResult>> {
|
2020-03-30 22:19:37 +08:00
|
|
|
self.eval_ast_with_scope(&mut Scope::new(), ast)
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Evaluate an `AST` with own scope.
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-19 13:52:10 +08:00
|
|
|
/// use rhai::{Engine, Scope};
|
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-19 13:52:10 +08:00
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Compile a script to an AST and store it for later evaluation
|
2020-03-19 13:52:10 +08:00
|
|
|
/// let ast = engine.compile("x + 2")?;
|
|
|
|
///
|
|
|
|
/// // Create initialized scope
|
|
|
|
/// let mut scope = Scope::new();
|
|
|
|
/// scope.push("x", 40_i64);
|
|
|
|
///
|
2020-03-19 19:53:42 +08:00
|
|
|
/// // Compile a script to an AST and store it for later evaluation
|
|
|
|
/// let ast = engine.compile("x = x + 2; x")?;
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// // Evaluate it
|
2020-03-19 19:53:42 +08: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 13:52:10 +08:00
|
|
|
///
|
|
|
|
/// // The variable in the scope is modified
|
|
|
|
/// assert_eq!(scope.get_value::<i64>("x").expect("variable x should exist"), 44);
|
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn eval_ast_with_scope<T: Variant + Clone>(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-02 14:28:42 +08:00
|
|
|
scope: &mut Scope,
|
|
|
|
ast: &AST,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<T, Box<EvalAltResult>> {
|
|
|
|
let result = self.eval_ast_with_scope_raw(scope, ast)?;
|
2020-04-18 00:14:33 +08:00
|
|
|
|
2020-04-13 10:27:08 +08:00
|
|
|
let return_type = self.map_type_name(result.type_name());
|
|
|
|
|
|
|
|
return result.try_cast::<T>().ok_or_else(|| {
|
2020-04-21 23:25:12 +08:00
|
|
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
return_type.to_string(),
|
|
|
|
Position::none(),
|
|
|
|
))
|
2020-04-13 10:27:08 +08:00
|
|
|
});
|
2020-03-19 19:53:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
pub(crate) fn eval_ast_with_scope_raw(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-19 19:53:42 +08:00
|
|
|
scope: &mut Scope,
|
|
|
|
ast: &AST,
|
2020-04-18 00:14:33 +08:00
|
|
|
) -> Result<Dynamic, Box<EvalAltResult>> {
|
2020-05-07 10:00:10 +08:00
|
|
|
let mut state = State::new(ast.fn_lib());
|
2020-04-28 23:05:03 +08:00
|
|
|
|
2020-05-05 15:00:10 +08:00
|
|
|
ast.statements()
|
2020-03-30 22:19:37 +08:00
|
|
|
.iter()
|
2020-04-22 12:12:13 +08:00
|
|
|
.try_fold(().into(), |_, stmt| {
|
2020-05-07 10:00:10 +08:00
|
|
|
self.eval_stmt(scope, &mut state, stmt, 0)
|
2020-04-07 13:23:06 +08:00
|
|
|
})
|
2020-04-18 00:14:33 +08:00
|
|
|
.or_else(|err| match *err {
|
2020-04-07 13:23:06 +08:00
|
|
|
EvalAltResult::Return(out, _) => Ok(out),
|
|
|
|
_ => Err(err),
|
|
|
|
})
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-04 22:00:01 +08:00
|
|
|
/// Evaluate a file, but throw away the result and only return error (if any).
|
2020-03-10 09:30:12 +08:00
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
2020-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn consume_file(&self, path: PathBuf) -> Result<(), Box<EvalAltResult>> {
|
2020-04-04 22:00:44 +08:00
|
|
|
Self::read_file(path).and_then(|contents| self.consume(&contents))
|
2020-03-15 22:39:58 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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-03-18 12:04:26 +08:00
|
|
|
#[cfg(not(feature = "no_std"))]
|
2020-03-15 22:39:58 +08:00
|
|
|
pub fn consume_file_with_scope(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-15 22:39:58 +08:00
|
|
|
scope: &mut Scope,
|
|
|
|
path: PathBuf,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<(), Box<EvalAltResult>> {
|
2020-04-04 22:00:44 +08:00
|
|
|
Self::read_file(path).and_then(|contents| self.consume_with_scope(scope, &contents))
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-04 22:00:01 +08:00
|
|
|
/// Evaluate a string, but throw away the result and only return error (if any).
|
2020-03-10 09:30:12 +08:00
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn consume(&self, script: &str) -> Result<(), Box<EvalAltResult>> {
|
2020-04-08 16:57:15 +08:00
|
|
|
self.consume_with_scope(&mut Scope::new(), script)
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-15 22:39:58 +08:00
|
|
|
/// Evaluate a string with own scope, but throw away the result and only return error (if any).
|
2020-03-10 09:30:12 +08:00
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
2020-04-21 23:25:12 +08:00
|
|
|
pub fn consume_with_scope(
|
|
|
|
&self,
|
|
|
|
scope: &mut Scope,
|
|
|
|
script: &str,
|
|
|
|
) -> Result<(), Box<EvalAltResult>> {
|
2020-04-10 17:14:07 +08:00
|
|
|
let scripts = [script];
|
|
|
|
let stream = lex(&scripts);
|
2020-04-10 21:02:38 +08:00
|
|
|
|
|
|
|
// Since the AST will be thrown away afterwards, don't bother to optimize it
|
|
|
|
let ast = parse(&mut stream.peekable(), self, scope, OptimizationLevel::None)?;
|
2020-04-04 22:00:44 +08:00
|
|
|
self.consume_ast_with_scope(scope, &ast)
|
2020-03-12 15:30:42 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
/// 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-21 23:25:12 +08:00
|
|
|
pub fn consume_ast(&self, ast: &AST) -> Result<(), Box<EvalAltResult>> {
|
2020-04-04 22:00:44 +08:00
|
|
|
self.consume_ast_with_scope(&mut Scope::new(), ast)
|
2020-03-15 22:39:58 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Evaluate an `AST` with own scope, but throw away the result and only return error (if any).
|
2020-03-15 22:39:58 +08:00
|
|
|
/// Useful for when you don't need the result, but still need to keep track of possible errors.
|
2020-03-12 15:30:42 +08:00
|
|
|
pub fn consume_ast_with_scope(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-03-12 15:30:42 +08:00
|
|
|
scope: &mut Scope,
|
|
|
|
ast: &AST,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<(), Box<EvalAltResult>> {
|
2020-05-07 10:00:10 +08:00
|
|
|
let mut state = State::new(ast.fn_lib());
|
2020-04-28 23:05:03 +08:00
|
|
|
|
2020-05-05 15:00:10 +08:00
|
|
|
ast.statements()
|
2020-03-12 14:54:14 +08:00
|
|
|
.iter()
|
2020-04-22 12:12:13 +08:00
|
|
|
.try_fold(().into(), |_, stmt| {
|
2020-05-07 10:00:10 +08:00
|
|
|
self.eval_stmt(scope, &mut state, stmt, 0)
|
2020-04-07 13:23:06 +08:00
|
|
|
})
|
2020-04-18 00:14:33 +08:00
|
|
|
.map_or_else(
|
|
|
|
|err| match *err {
|
|
|
|
EvalAltResult::Return(_, _) => Ok(()),
|
2020-04-21 23:25:12 +08:00
|
|
|
err => Err(Box::new(err)),
|
2020-04-18 00:14:33 +08:00
|
|
|
},
|
|
|
|
|_| Ok(()),
|
|
|
|
)
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-04-04 22:00:44 +08:00
|
|
|
/// Call a script function defined in an `AST` with multiple arguments.
|
2020-03-04 22:00:01 +08:00
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-03-11 13:28:12 +08:00
|
|
|
/// # #[cfg(not(feature = "no_function"))]
|
2020-03-10 19:48:47 +08:00
|
|
|
/// # {
|
2020-04-05 12:17:31 +08:00
|
|
|
/// use rhai::{Engine, Scope};
|
2020-03-09 21:57:07 +08:00
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// let engine = Engine::new();
|
2020-03-04 22:00:01 +08:00
|
|
|
///
|
2020-04-07 21:50:33 +08:00
|
|
|
/// let ast = engine.compile(r"
|
|
|
|
/// fn add(x, y) { len(x) + y + foo }
|
|
|
|
/// fn add1(x) { len(x) + 1 + foo }
|
|
|
|
/// fn bar() { foo/2 }
|
|
|
|
/// ")?;
|
2020-04-05 12:17:31 +08:00
|
|
|
///
|
|
|
|
/// let mut scope = Scope::new();
|
|
|
|
/// scope.push("foo", 42_i64);
|
2020-03-04 22:00:01 +08:00
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// // Call the script-defined function
|
2020-04-07 21:50:33 +08:00
|
|
|
/// let result: i64 = engine.call_fn(&mut scope, &ast, "add", ( String::from("abc"), 123_i64 ) )?;
|
2020-04-05 12:17:31 +08:00
|
|
|
/// assert_eq!(result, 168);
|
2020-04-07 21:50:33 +08:00
|
|
|
///
|
|
|
|
/// let result: i64 = engine.call_fn(&mut scope, &ast, "add1", ( String::from("abc"), ) )?;
|
|
|
|
/// // ^^^^^^^^^^^^^^^^^^^^^^^^ tuple of one
|
|
|
|
/// assert_eq!(result, 46);
|
|
|
|
///
|
|
|
|
/// let result: i64 = engine.call_fn(&mut scope, &ast, "bar", () )?;
|
|
|
|
/// assert_eq!(result, 21);
|
2020-03-10 19:48:47 +08:00
|
|
|
/// # }
|
2020-03-04 22:00:01 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-03-11 13:28:12 +08:00
|
|
|
#[cfg(not(feature = "no_function"))]
|
2020-04-12 23:00:06 +08:00
|
|
|
pub fn call_fn<A: FuncArgs, T: Variant + Clone>(
|
2020-04-07 13:23:06 +08:00
|
|
|
&self,
|
2020-04-05 12:17:31 +08:00
|
|
|
scope: &mut Scope,
|
2020-04-04 22:00:44 +08:00
|
|
|
ast: &AST,
|
2020-03-04 22:00:01 +08:00
|
|
|
name: &str,
|
|
|
|
args: A,
|
2020-04-21 23:25:12 +08:00
|
|
|
) -> Result<T, Box<EvalAltResult>> {
|
2020-04-07 21:50:33 +08:00
|
|
|
let mut arg_values = args.into_vec();
|
2020-05-10 21:25:47 +08:00
|
|
|
let mut args: StaticVec<_> = arg_values.iter_mut().collect();
|
2020-05-05 15:00:10 +08:00
|
|
|
let fn_lib = ast.fn_lib();
|
2020-04-07 13:23:06 +08:00
|
|
|
let pos = Position::none();
|
2020-03-10 17:10:33 +08:00
|
|
|
|
2020-04-24 12:39:24 +08:00
|
|
|
let fn_def = fn_lib
|
2020-05-09 11:29:30 +08:00
|
|
|
.get_function_by_signature(name, args.len(), true)
|
2020-04-24 12:39:24 +08:00
|
|
|
.ok_or_else(|| Box::new(EvalAltResult::ErrorFunctionNotFound(name.to_string(), pos)))?;
|
|
|
|
|
2020-05-07 10:00:10 +08:00
|
|
|
let state = State::new(fn_lib);
|
|
|
|
|
2020-05-10 21:25:47 +08:00
|
|
|
let result = self.call_script_fn(Some(scope), &state, fn_def, args.as_mut(), pos, 0)?;
|
2020-04-18 00:14:33 +08:00
|
|
|
|
2020-04-13 10:27:08 +08:00
|
|
|
let return_type = self.map_type_name(result.type_name());
|
|
|
|
|
2020-04-21 23:25:12 +08:00
|
|
|
return result.try_cast().ok_or_else(|| {
|
|
|
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
|
|
|
return_type.into(),
|
|
|
|
pos,
|
|
|
|
))
|
|
|
|
});
|
2020-03-04 22:00:01 +08:00
|
|
|
}
|
|
|
|
|
2020-03-19 13:52:10 +08:00
|
|
|
/// Optimize the `AST` with constants defined in an external Scope.
|
2020-04-05 17:44:48 +08:00
|
|
|
/// An optimized copy of the `AST` is returned while the original `AST` is consumed.
|
2020-03-15 22:39:58 +08:00
|
|
|
///
|
|
|
|
/// 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 13:52:10 +08:00
|
|
|
/// external scope, it will be more efficient to optimize the `AST` once again to take advantage
|
2020-03-15 22:39:58 +08:00
|
|
|
/// of the new constants.
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// With this method, it is no longer necessary to recompile a large script. The script `AST` can be
|
2020-03-15 22:39:58 +08:00
|
|
|
/// compiled just once. Before evaluation, constants are passed into the `Engine` via an external scope
|
2020-03-19 13:52:10 +08:00
|
|
|
/// (i.e. with `scope.push_constant(...)`). Then, the `AST is cloned and the copy re-optimized before running.
|
2020-03-15 22:39:58 +08:00
|
|
|
#[cfg(not(feature = "no_optimize"))]
|
2020-04-08 09:30:50 +08:00
|
|
|
pub fn optimize_ast(
|
|
|
|
&self,
|
|
|
|
scope: &Scope,
|
2020-05-05 15:00:10 +08:00
|
|
|
mut ast: AST,
|
2020-04-08 09:30:50 +08:00
|
|
|
optimization_level: OptimizationLevel,
|
|
|
|
) -> AST {
|
2020-04-16 23:58:57 +08:00
|
|
|
let fn_lib = ast
|
2020-05-05 15:00:10 +08:00
|
|
|
.fn_lib()
|
2020-04-16 23:58:57 +08:00
|
|
|
.iter()
|
|
|
|
.map(|(_, fn_def)| fn_def.as_ref().clone())
|
|
|
|
.collect();
|
2020-05-05 15:00:10 +08:00
|
|
|
|
|
|
|
let stmt = mem::take(ast.statements_mut());
|
|
|
|
optimize_into_ast(self, scope, stmt, fn_lib, optimization_level)
|
2020-03-15 22:39:58 +08:00
|
|
|
}
|
|
|
|
|
2020-03-04 22:00:01 +08:00
|
|
|
/// Override default action of `print` (print to stdout using `println!`)
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-04-07 13:23:06 +08:00
|
|
|
/// # use std::sync::RwLock;
|
2020-04-16 23:31:48 +08:00
|
|
|
/// # use std::sync::Arc;
|
2020-03-09 21:57:07 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let result = Arc::new(RwLock::new(String::from("")));
|
2020-04-07 13:23:06 +08:00
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// // Override action of 'print' function
|
|
|
|
/// let logger = result.clone();
|
|
|
|
/// engine.on_print(move |s| logger.write().unwrap().push_str(s));
|
|
|
|
///
|
|
|
|
/// engine.consume("print(40 + 2);")?;
|
2020-03-04 22:00:01 +08:00
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// assert_eq!(*result.read().unwrap(), "42");
|
2020-03-09 21:57:07 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
2020-03-04 22:00:01 +08:00
|
|
|
/// ```
|
2020-04-03 17:17:00 +08:00
|
|
|
#[cfg(feature = "sync")]
|
2020-04-16 23:31:48 +08:00
|
|
|
pub fn on_print(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
2020-04-27 21:28:31 +08:00
|
|
|
self.print = Box::new(callback);
|
2020-04-03 17:17:00 +08:00
|
|
|
}
|
2020-04-03 17:18:30 +08:00
|
|
|
/// Override default action of `print` (print to stdout using `println!`)
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-04-07 13:23:06 +08:00
|
|
|
/// # use std::sync::RwLock;
|
2020-04-16 23:31:48 +08:00
|
|
|
/// # use std::sync::Arc;
|
2020-04-03 17:18:30 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let result = Arc::new(RwLock::new(String::from("")));
|
2020-04-07 13:23:06 +08:00
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// // Override action of 'print' function
|
|
|
|
/// let logger = result.clone();
|
|
|
|
/// engine.on_print(move |s| logger.write().unwrap().push_str(s));
|
|
|
|
///
|
|
|
|
/// engine.consume("print(40 + 2);")?;
|
2020-04-03 17:18:30 +08:00
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// assert_eq!(*result.read().unwrap(), "42");
|
2020-04-03 17:18:30 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-03 17:17:00 +08:00
|
|
|
#[cfg(not(feature = "sync"))]
|
2020-04-16 23:31:48 +08:00
|
|
|
pub fn on_print(&mut self, callback: impl Fn(&str) + 'static) {
|
2020-04-27 21:28:31 +08:00
|
|
|
self.print = Box::new(callback);
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
|
2020-03-04 22:00:01 +08:00
|
|
|
/// Override default action of `debug` (print to stdout using `println!`)
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
2020-03-19 13:52:10 +08:00
|
|
|
/// ```
|
2020-04-23 13:22:53 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-04-07 13:23:06 +08:00
|
|
|
/// # use std::sync::RwLock;
|
2020-04-16 23:31:48 +08:00
|
|
|
/// # use std::sync::Arc;
|
2020-03-09 21:57:07 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let result = Arc::new(RwLock::new(String::from("")));
|
2020-03-04 22:00:01 +08:00
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// // Override action of 'print' function
|
|
|
|
/// let logger = result.clone();
|
|
|
|
/// engine.on_debug(move |s| logger.write().unwrap().push_str(s));
|
|
|
|
///
|
|
|
|
/// engine.consume(r#"debug("hello");"#)?;
|
2020-04-07 13:23:06 +08:00
|
|
|
///
|
|
|
|
/// assert_eq!(*result.read().unwrap(), r#""hello""#);
|
2020-03-09 21:57:07 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
2020-03-04 22:00:01 +08:00
|
|
|
/// ```
|
2020-04-03 17:17:00 +08:00
|
|
|
#[cfg(feature = "sync")]
|
2020-04-16 23:31:48 +08:00
|
|
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + Send + Sync + 'static) {
|
2020-04-27 21:28:31 +08:00
|
|
|
self.debug = Box::new(callback);
|
2020-04-03 17:17:00 +08:00
|
|
|
}
|
2020-04-03 17:18:30 +08:00
|
|
|
/// Override default action of `debug` (print to stdout using `println!`)
|
|
|
|
///
|
|
|
|
/// # Example
|
|
|
|
///
|
|
|
|
/// ```
|
2020-04-21 23:25:12 +08:00
|
|
|
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
|
2020-04-07 13:23:06 +08:00
|
|
|
/// # use std::sync::RwLock;
|
2020-04-16 23:31:48 +08:00
|
|
|
/// # use std::sync::Arc;
|
2020-04-03 17:18:30 +08:00
|
|
|
/// use rhai::Engine;
|
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let result = Arc::new(RwLock::new(String::from("")));
|
2020-04-07 13:23:06 +08:00
|
|
|
///
|
2020-04-16 23:31:48 +08:00
|
|
|
/// let mut engine = Engine::new();
|
|
|
|
///
|
|
|
|
/// // Override action of 'print' function
|
|
|
|
/// let logger = result.clone();
|
|
|
|
/// engine.on_debug(move |s| logger.write().unwrap().push_str(s));
|
|
|
|
///
|
|
|
|
/// engine.consume(r#"debug("hello");"#)?;
|
2020-04-03 17:18:30 +08:00
|
|
|
///
|
2020-04-07 13:23:06 +08:00
|
|
|
/// assert_eq!(*result.read().unwrap(), r#""hello""#);
|
2020-04-03 17:18:30 +08:00
|
|
|
/// # Ok(())
|
|
|
|
/// # }
|
|
|
|
/// ```
|
2020-04-03 17:17:00 +08:00
|
|
|
#[cfg(not(feature = "sync"))]
|
2020-04-16 23:31:48 +08:00
|
|
|
pub fn on_debug(&mut self, callback: impl Fn(&str) + 'static) {
|
2020-04-27 21:28:31 +08:00
|
|
|
self.debug = Box::new(callback);
|
2020-03-02 14:28:42 +08:00
|
|
|
}
|
|
|
|
}
|