Bump version to 0.12.0.
This commit is contained in:
parent
34ef2d6e00
commit
3609150dcf
@ -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"
|
||||||
|
22
README.md
22
README.md
@ -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`
|
||||||
|
@ -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,
|
||||||
|
@ -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),*);
|
||||||
|
@ -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
|
||||||
//! );
|
//! );
|
||||||
|
@ -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
|
||||||
|
@ -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 }",
|
||||||
|
@ -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::<()>(
|
||||||
|
Loading…
Reference in New Issue
Block a user