Bump version to 0.12.0.

This commit is contained in:
Stephen Chung 2020-04-09 18:45:49 +08:00
parent 34ef2d6e00
commit 3609150dcf
8 changed files with 67 additions and 63 deletions

View File

@ -1,6 +1,6 @@
[package] [package]
name = "rhai" name = "rhai"
version = "0.11.0" version = "0.12.0"
edition = "2018" edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"] authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
description = "Embedded scripting for Rust" description = "Embedded scripting for Rust"

View File

@ -27,7 +27,7 @@ Rhai's current features set:
to do checked arithmetic operations); for [`no_std`] builds, a number of additional dependencies are to do checked arithmetic operations); for [`no_std`] builds, a number of additional dependencies are
pulled in to provide for functionalities that used to be in `std`. pulled in to provide for functionalities that used to be in `std`.
**Note:** Currently, the version is 0.11.0, so the language and API's may change before they stabilize. **Note:** Currently, the version is 0.12.0, so the language and API's may change before they stabilize.
Installation Installation
------------ ------------
@ -36,7 +36,7 @@ Install the Rhai crate by adding this line to `dependencies`:
```toml ```toml
[dependencies] [dependencies]
rhai = "0.11.0" rhai = "0.12.0"
``` ```
Use the latest released crate version on [`crates.io`](https::/crates.io/crates/rhai/): Use the latest released crate version on [`crates.io`](https::/crates.io/crates/rhai/):
@ -207,7 +207,7 @@ let ast = engine.compile("40 + 2")?;
for _ in 0..42 { for _ in 0..42 {
let result: i64 = engine.eval_ast(&ast)?; let result: i64 = engine.eval_ast(&ast)?;
println!("Answer #{}: {}", i, result); // prints 42 println!("Answer #{}: {}", i, result); // prints 42
} }
``` ```
@ -263,9 +263,10 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )?
[`Func`]: #creating-rust-anonymous-functions-from-rhai-script [`Func`]: #creating-rust-anonymous-functions-from-rhai-script
It is possible to further encapsulate a script in Rust such that it essentially becomes a normal Rust function. It is possible to further encapsulate a script in Rust such that it becomes a normal Rust function.
This is accomplished via the `Func` trait which contains `create_from_script` (as well as its associate Such an _anonymous function_ is basically a boxed closure, very useful as call-back functions.
method `create_from_ast`): Creating them is accomplished via the `Func` trait which contains `create_from_script`
(as well as its companion method `create_from_ast`):
```rust ```rust
use rhai::{Engine, Func}; // use 'Func' for 'create_from_script' use rhai::{Engine, Func}; // use 'Func' for 'create_from_script'
@ -288,6 +289,15 @@ let func = Func::<(i64, String), bool>::create_from_script(
)?; )?;
func(123, "hello".to_string())? == false; // call the anonymous function func(123, "hello".to_string())? == false; // call the anonymous function
schedule_callback(func); // pass it as a callback to another function
// Although there is nothing you can't do by manually writing out the closure yourself...
let engine = Engine::new();
let ast = engine.compile(script)?;
schedule_callback(Box::new(move |x: i64, y: String| -> Result<bool, EvalAltResult> {
engine.call_fn(&mut Scope::new(), &ast, "calc", (x, y))
}));
``` ```
Raw `Engine` Raw `Engine`

View File

@ -50,7 +50,7 @@ type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>> + Send +
type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>; type IteratorFn = dyn Fn(&Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
#[cfg(debug_assertions)] #[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 42; pub const MAX_CALL_STACK_DEPTH: usize = 32;
#[cfg(not(debug_assertions))] #[cfg(not(debug_assertions))]
pub const MAX_CALL_STACK_DEPTH: usize = 256; pub const MAX_CALL_STACK_DEPTH: usize = 256;
@ -277,7 +277,7 @@ pub struct Engine<'e> {
/// Maximum levels of call-stack to prevent infinite recursion. /// Maximum levels of call-stack to prevent infinite recursion.
/// ///
/// Defaults to 42 for debug builds and 256 for non-debug builds. /// Defaults to 32 for debug builds and 256 for non-debug builds.
pub(crate) max_call_stack_depth: usize, pub(crate) max_call_stack_depth: usize,
} }
@ -372,11 +372,6 @@ impl Engine<'_> {
/// Create a new `Engine` /// Create a new `Engine`
pub fn new() -> Self { pub fn new() -> Self {
// fn abc<F: Fn() + Send + Sync>(f: F) {
// f();
// }
// abc(|| ());
Default::default() Default::default()
} }
@ -420,32 +415,6 @@ impl Engine<'_> {
self.max_call_stack_depth = levels self.max_call_stack_depth = levels
} }
/// Call a registered function
#[cfg(not(feature = "no_optimize"))]
pub(crate) fn call_ext_fn_raw(
&self,
fn_name: &str,
args: &mut FnCallArgs,
pos: Position,
) -> Result<Option<Dynamic>, EvalAltResult> {
let spec = FnSpec {
name: fn_name.into(),
args: args.iter().map(|a| Any::type_id(*a)).collect(),
};
// Search built-in's and external functions
if let Some(functions) = &self.functions {
if let Some(func) = functions.get(&spec) {
// Run external function
Ok(Some(func(args, pos)?))
} else {
Ok(None)
}
} else {
Ok(None)
}
}
/// Universal method for calling functions either registered with the `Engine` or written in Rhai /// Universal method for calling functions either registered with the `Engine` or written in Rhai
pub(crate) fn call_fn_raw( pub(crate) fn call_fn_raw(
&self, &self,

View File

@ -132,9 +132,9 @@ macro_rules! def_register {
def_register!(imp); def_register!(imp);
}; };
(imp $($par:ident => $mark:ty => $param:ty => $clone:expr),*) => { (imp $($par:ident => $mark:ty => $param:ty => $clone:expr),*) => {
// ^ function parameter generic type name // ^ function parameter generic type name (A, B, C etc.)
// ^ function parameter marker type (T, Ref<T> or Mut<T>) // ^ function parameter marker type (T, Ref<T> or Mut<T>)
// ^ function parameter actual type // ^ function parameter actual type (T, &T or &mut T)
// ^ dereferencing function // ^ dereferencing function
impl< impl<
$($par: Any + Clone,)* $($par: Any + Clone,)*
@ -171,6 +171,7 @@ macro_rules! def_register {
let r = f($(($clone)($par)),*); let r = f($(($clone)($par)),*);
Ok(Box::new(r) as Dynamic) Ok(Box::new(r) as Dynamic)
}; };
self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func)); self.register_fn_raw(name, vec![$(TypeId::of::<$par>()),*], Box::new(func));
} }
} }
@ -255,9 +256,10 @@ macro_rules! def_register {
def_register!(imp $p0 => $p0 => $p0 => Clone::clone $(, $p => $p => $p => Clone::clone)*); def_register!(imp $p0 => $p0 => $p0 => Clone::clone $(, $p => $p => $p => Clone::clone)*);
def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => Clone::clone)*); def_register!(imp $p0 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => Clone::clone)*);
// handle the first parameter ^ first parameter passed through // handle the first parameter ^ first parameter passed through
// others passed by value (cloned) ^ // ^ others passed by value (cloned)
// No support for functions where the first argument is a reference // Currently does not support first argument which is a reference, as there will be
// conflicting implementations since &T: Any and T: Any cannot be distinguished
//def_register!(imp $p0 => Ref<$p0> => &$p0 => identity $(, $p => $p => $p => Clone::clone)*); //def_register!(imp $p0 => Ref<$p0> => &$p0 => identity $(, $p => $p => $p => Clone::clone)*);
def_register!($($p),*); def_register!($($p),*);

View File

@ -38,6 +38,7 @@
//! //!
//! # #[cfg(not(feature = "no_std"))] //! # #[cfg(not(feature = "no_std"))]
//! assert_eq!( //! assert_eq!(
//! // Evaluate the script, expects a 'bool' return
//! engine.eval_file::<bool>("my_script.rhai".into())?, //! engine.eval_file::<bool>("my_script.rhai".into())?,
//! true //! true
//! ); //! );

View File

@ -2,14 +2,16 @@
use crate::any::{Any, Dynamic}; use crate::any::{Any, Dynamic};
use crate::engine::{ use crate::engine::{
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_EVAL, KEYWORD_PRINT, Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_EVAL,
KEYWORD_TYPE_OF, KEYWORD_PRINT, KEYWORD_TYPE_OF,
}; };
use crate::parser::{map_dynamic_to_expr, Expr, FnDef, ReturnType, Stmt, AST}; use crate::parser::{map_dynamic_to_expr, Expr, FnDef, Position, ReturnType, Stmt, AST};
use crate::result::EvalAltResult;
use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope}; use crate::scope::{Entry as ScopeEntry, EntryType as ScopeEntryType, Scope};
use crate::stdlib::{ use crate::stdlib::{
boxed::Box, boxed::Box,
collections::HashMap,
rc::Rc, rc::Rc,
string::{String, ToString}, string::{String, ToString},
sync::Arc, sync::Arc,
@ -96,6 +98,25 @@ impl<'a> State<'a> {
} }
} }
/// Call a registered function
fn call_fn(
functions: Option<&HashMap<FnSpec, Box<FnAny>>>,
fn_name: &str,
args: &mut FnCallArgs,
pos: Position,
) -> Result<Option<Dynamic>, EvalAltResult> {
let spec = FnSpec {
name: fn_name.into(),
args: args.iter().map(|a| Any::type_id(*a)).collect(),
};
// Search built-in's and external functions
functions
.and_then(|f| f.get(&spec))
.map(|func| func(args, pos))
.transpose()
}
/// Optimize a statement. /// Optimize a statement.
fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt { fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> Stmt {
match stmt { match stmt {
@ -528,21 +549,25 @@ fn optimize_expr<'a>(expr: Expr, state: &mut State<'a>) -> Expr {
"" ""
}; };
state.engine.call_ext_fn_raw(&id, &mut call_args, pos).ok().map(|result| call_fn(state.engine.functions.as_ref(), &id, &mut call_args, pos).ok()
result.or_else(|| { .and_then(|result|
if !arg_for_type_of.is_empty() { result.or_else(|| {
// Handle `type_of()` if !arg_for_type_of.is_empty() {
Some(arg_for_type_of.to_string().into_dynamic()) // Handle `type_of()`
} else { Some(arg_for_type_of.to_string().into_dynamic())
// Otherwise use the default value, if any } else {
def_value.clone() // Otherwise use the default value, if any
} def_value.clone()
}).and_then(|result| map_dynamic_to_expr(result, pos)) }
}).and_then(|result| map_dynamic_to_expr(result, pos))
.map(|expr| { .map(|expr| {
state.set_dirty(); state.set_dirty();
expr expr
}) })
).flatten().unwrap_or_else(|| Expr::FunctionCall(id, args, def_value, pos)) ).unwrap_or_else(||
// Optimize function call arguments
Expr::FunctionCall(id, args.into_iter().map(|a| optimize_expr(a, state)).collect(), def_value, pos)
)
} }
// id(args ..) -> optimize function call arguments // id(args ..) -> optimize function call arguments

View File

@ -62,9 +62,6 @@ fn test_call_fn() -> Result<(), EvalAltResult> {
#[test] #[test]
fn test_anonymous_fn() -> Result<(), EvalAltResult> { fn test_anonymous_fn() -> Result<(), EvalAltResult> {
let calc_func: Box<dyn Fn(INT, INT, INT) -> Result<INT, EvalAltResult>> =
Engine::new().create_from_script("fn calc() { 42 }", "calc")?;
let calc_func = Func::<(INT, INT, INT), INT>::create_from_script( let calc_func = Func::<(INT, INT, INT), INT>::create_from_script(
Engine::new(), Engine::new(),
"fn calc(x, y, z) { (x + y) * z }", "fn calc(x, y, z) { (x + y) * z }",

View File

@ -9,10 +9,10 @@ fn test_stack_overflow() -> Result<(), EvalAltResult> {
engine.eval::<i64>( engine.eval::<i64>(
r" r"
fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } } fn foo(n) { if n == 0 { 0 } else { n + foo(n-1) } }
foo(38) foo(30)
", ",
)?, )?,
741 465
); );
match engine.eval::<()>( match engine.eval::<()>(