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]
name = "rhai"
version = "0.11.0"
version = "0.12.0"
edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung"]
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
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
------------
@ -36,7 +36,7 @@ Install the Rhai crate by adding this line to `dependencies`:
```toml
[dependencies]
rhai = "0.11.0"
rhai = "0.12.0"
```
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 {
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
It is possible to further encapsulate a script in Rust such that it essentially becomes a normal Rust function.
This is accomplished via the `Func` trait which contains `create_from_script` (as well as its associate
method `create_from_ast`):
It is possible to further encapsulate a script in Rust such that it becomes a normal Rust function.
Such an _anonymous function_ is basically a boxed closure, very useful as call-back functions.
Creating them is accomplished via the `Func` trait which contains `create_from_script`
(as well as its companion method `create_from_ast`):
```rust
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
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`

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>>;
#[cfg(debug_assertions)]
pub const MAX_CALL_STACK_DEPTH: usize = 42;
pub const MAX_CALL_STACK_DEPTH: usize = 32;
#[cfg(not(debug_assertions))]
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.
///
/// 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,
}
@ -372,11 +372,6 @@ impl Engine<'_> {
/// Create a new `Engine`
pub fn new() -> Self {
// fn abc<F: Fn() + Send + Sync>(f: F) {
// f();
// }
// abc(|| ());
Default::default()
}
@ -420,32 +415,6 @@ impl Engine<'_> {
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
pub(crate) fn call_fn_raw(
&self,

View File

@ -132,9 +132,9 @@ macro_rules! def_register {
def_register!(imp);
};
(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 actual type
// ^ function parameter actual type (T, &T or &mut T)
// ^ dereferencing function
impl<
$($par: Any + Clone,)*
@ -171,6 +171,7 @@ macro_rules! def_register {
let r = f($(($clone)($par)),*);
Ok(Box::new(r) as Dynamic)
};
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 => Mut<$p0> => &mut $p0 => identity $(, $p => $p => $p => Clone::clone)*);
// 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!($($p),*);

View File

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

View File

@ -2,14 +2,16 @@
use crate::any::{Any, Dynamic};
use crate::engine::{
Engine, FunctionsLib, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_EVAL, KEYWORD_PRINT,
KEYWORD_TYPE_OF,
Engine, FnAny, FnCallArgs, FnSpec, FunctionsLib, KEYWORD_DEBUG, KEYWORD_DUMP_AST, KEYWORD_EVAL,
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::stdlib::{
boxed::Box,
collections::HashMap,
rc::Rc,
string::{String, ToString},
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.
fn optimize_stmt<'a>(stmt: Stmt, state: &mut State<'a>, preserve_result: bool) -> 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|
result.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into_dynamic())
} else {
// Otherwise use the default value, if any
def_value.clone()
}
}).and_then(|result| map_dynamic_to_expr(result, pos))
call_fn(state.engine.functions.as_ref(), &id, &mut call_args, pos).ok()
.and_then(|result|
result.or_else(|| {
if !arg_for_type_of.is_empty() {
// Handle `type_of()`
Some(arg_for_type_of.to_string().into_dynamic())
} else {
// Otherwise use the default value, if any
def_value.clone()
}
}).and_then(|result| map_dynamic_to_expr(result, pos))
.map(|expr| {
state.set_dirty();
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

View File

@ -62,9 +62,6 @@ fn test_call_fn() -> Result<(), EvalAltResult> {
#[test]
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(
Engine::new(),
"fn calc(x, y, z) { (x + y) * z }",

View File

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