diff --git a/README.md b/README.md index 31fe5dc2..2d801179 100644 --- a/README.md +++ b/README.md @@ -261,26 +261,26 @@ let result: i64 = engine.call_fn(&mut scope, &ast, "hello", () )? ### Creating Rust anonymous functions from Rhai script -[`AnonymousFn`]: #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. -This is accomplished via the `AnonymousFn` trait which contains `create_from_script` (as well as its associate +This is accomplished via the `Func` trait which contains `create_from_script` (as well as its associate method `create_from_ast`): ```rust -use rhai::{Engine, AnonymousFn}; // use 'AnonymousFn' for 'create_from_script' +use rhai::{Engine, Func}; // use 'Func' for 'create_from_script' let engine = Engine::new(); // create a new 'Engine' just for this let script = "fn calc(x, y) { x + y.len() < 42 }"; -// AnonymousFn takes two type parameters: +// Func takes two type parameters: // 1) a tuple made up of the types of the script function's parameters // 2) the return type of the script function // // 'func' will have type Box Result> and is callable! -let func = AnonymousFn::<(i64, String), bool>::create_from_script( -// ^^^^^^^^^^^^^ function parameter types in tuple +let func = Func::<(i64, String), bool>::create_from_script( +// ^^^^^^^^^^^^^ function parameter types in tuple engine, // the 'Engine' is consumed into the closure script, // the script, notice number of parameters must match @@ -336,6 +336,7 @@ Values and types [`type_of()`]: #values-and-types [`to_string()`]: #values-and-types +[`()`]: #values-and-types The following primitive types are supported natively: @@ -353,8 +354,6 @@ The following primitive types are supported natively: | **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. | | **Nothing/void/nil/null** (or whatever you want to call it) | `()` | `"()"` | `""` _(empty string)_ | -[`()`]: #values-and-types - All types are treated strictly separate by Rhai, meaning that `i32` and `i64` and `u32` are completely different - they even cannot be added together. This is very similar to Rust. @@ -479,6 +478,20 @@ let c = 'X'; // character print("c is '" + c + "' and its code is " + c.to_int()); // prints "c is 'X' and its code is 88" ``` +Traits +------ + +A number of traits, under the `rhai::` module namespace, provide additional functionalities. + +| Trait | Description | Methods | +| ------------------- | --------------------------------------------------------------------------------- | --------------------------------------- | +| `Any` | Generic trait that represents a [`Dynamic`] type | `type_id`, `type_name`, `into_dynamic` | +| `AnyExt` | Extension trait to allows casting of a [`Dynamic`] value to Rust types | `cast`, `try_cast` | +| `RegisterFn` | Trait for registering functions | `register_fn` | +| `RegisterDynamicFn` | Trait for registering functions returning [`Dynamic`] | `register_dynamic_fn` | +| `RegisterResultFn` | Trait for registering fallible functions returning `Result<`_T_`, EvalAltResult>` | `register_result_fn` | +| `Func` | Trait for creating anonymous functions from script | `create_from_ast`, `create_from_script` | + Working with functions ---------------------- @@ -1245,7 +1258,7 @@ Property values can be accessed via the dot notation (_object_ `.` _property_) o The dot notation allows only property names that follow the same naming rules as [variables]. The index notation allows setting/getting properties of arbitrary names (even the empty string). -**Important:** Trying to read a non-existent property returns `()` instead of causing an error. +**Important:** Trying to read a non-existent property returns [`()`] instead of causing an error. The Rust type of a Rhai object map is `rhai::Map`. [`type_of()`] an object map returns `"map"`. @@ -1620,6 +1633,10 @@ fn do_addition(x) { } ``` +Unlike C/C++, functions can be defined _anywhere_ within the global level. A function does not need to be defined +prior to being used in a script; a statement in the script can freely call a function defined afterwards. +This is similar to Rust and many other modern languages. + ### Functions overloading Functions can be _overloaded_ and are resolved purely upon the function's _name_ and the _number_ of parameters diff --git a/src/fn_anonymous.rs b/src/fn_func.rs similarity index 80% rename from src/fn_anonymous.rs rename to src/fn_func.rs index ecdfff25..7350d6d5 100644 --- a/src/fn_anonymous.rs +++ b/src/fn_func.rs @@ -9,8 +9,10 @@ use crate::parser::AST; use crate::result::EvalAltResult; use crate::scope::Scope; +use crate::stdlib::{boxed::Box, string::ToString}; + /// A trait to create a Rust anonymous function from a script. -pub trait AnonymousFn { +pub trait Func { type Output; /// Create a Rust anonymous function from an `AST`. @@ -20,19 +22,19 @@ pub trait AnonymousFn { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { - /// use rhai::{Engine, AnonymousFn}; // use 'AnonymousFn' for 'create_from_ast' + /// use rhai::{Engine, Func}; // use 'Func' for 'create_from_ast' /// /// let engine = Engine::new(); // create a new 'Engine' just for this /// /// let ast = engine.compile("fn calc(x, y) { x + y.len() < 42 }")?; /// - /// // AnonymousFn takes two type parameters: + /// // Func takes two type parameters: /// // 1) a tuple made up of the types of the script function's parameters /// // 2) the return type of the script function /// // /// // 'func' will have type Box Result> and is callable! - /// let func = AnonymousFn::<(i64, String), bool>::create_from_ast( - /// // ^^^^^^^^^^^^^ function parameter types in tuple + /// let func = Func::<(i64, String), bool>::create_from_ast( + /// // ^^^^^^^^^^^^^ function parameter types in tuple /// /// engine, // the 'Engine' is consumed into the closure /// ast, // the 'AST' @@ -51,19 +53,19 @@ pub trait AnonymousFn { /// /// ``` /// # fn main() -> Result<(), rhai::EvalAltResult> { - /// use rhai::{Engine, AnonymousFn}; // use 'AnonymousFn' for 'create_from_script' + /// use rhai::{Engine, Func}; // use 'Func' for 'create_from_script' /// /// let engine = Engine::new(); // create a new 'Engine' just for this /// /// let script = "fn calc(x, y) { x + y.len() < 42 }"; /// - /// // AnonymousFn takes two type parameters: + /// // Func takes two type parameters: /// // 1) a tuple made up of the types of the script function's parameters /// // 2) the return type of the script function /// // /// // 'func' will have type Box Result> and is callable! - /// let func = AnonymousFn::<(i64, String), bool>::create_from_script( - /// // ^^^^^^^^^^^^^ function parameter types in tuple + /// let func = Func::<(i64, String), bool>::create_from_script( + /// // ^^^^^^^^^^^^^ function parameter types in tuple /// /// engine, // the 'Engine' is consumed into the closure /// script, // the script, notice number of parameters must match @@ -86,7 +88,7 @@ macro_rules! def_anonymous_fn { def_anonymous_fn!(imp); }; (imp $($par:ident),*) => { - impl<'e, $($par: Any + Clone,)* RET: Any + Clone> AnonymousFn<($($par,)*), RET> for Engine<'e> + impl<'e, $($par: Any + Clone,)* RET: Any + Clone> Func<($($par,)*), RET> for Engine<'e> { #[cfg(feature = "sync")] type Output = Box Result + Send + Sync + 'e>; @@ -98,13 +100,13 @@ macro_rules! def_anonymous_fn { let name = entry_point.to_string(); Box::new(move |$($par: $par),*| { - self.call_fn::<_, RET>(&mut Scope::new(), &ast, &name, ($($par,)*)) + self.call_fn(&mut Scope::new(), &ast, &name, ($($par,)*)) }) } fn create_from_script(self, script: &str, entry_point: &str) -> Result { let ast = self.compile(script)?; - Ok(AnonymousFn::<($($par,)*), RET>::create_from_ast(self, ast, entry_point)) + Ok(Func::<($($par,)*), RET>::create_from_ast(self, ast, entry_point)) } } }; diff --git a/src/lib.rs b/src/lib.rs index e61ad90a..47832e87 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3,15 +3,19 @@ //! Rhai is a tiny, simple and very fast embedded scripting language for Rust //! that gives you a safe and easy way to add scripting to your applications. //! It provides a familiar syntax based on JS and Rust and a simple Rust interface. -//! Here is a quick example. First, the contents of `my_script.rhai`: +//! Here is a quick example. +//! +//! First, the contents of `my_script.rhai`: //! //! ```,ignore +//! // Brute force factorial function //! fn factorial(x) { //! if x == 1 { return 1; } //! x * factorial(x - 1) //! } //! -//! compute_something(factorial(10)) +//! // Calling an external function 'compute' +//! compute(factorial(10)) //! ``` //! //! And the Rust part: @@ -21,16 +25,22 @@ //! //! fn main() -> Result<(), EvalAltResult> //! { +//! // Define external function //! fn compute_something(x: i64) -> bool { //! (x % 40) == 0 //! } //! +//! // Create scripting engine //! let mut engine = Engine::new(); //! -//! engine.register_fn("compute_something", compute_something); +//! // Register external function as 'compute' +//! engine.register_fn("compute", compute_something); //! //! # #[cfg(not(feature = "no_std"))] -//! assert_eq!(engine.eval_file::("my_script.rhai".into())?, true); +//! assert_eq!( +//! engine.eval_file::("my_script.rhai".into())?, +//! true +//! ); //! //! Ok(()) //! } @@ -63,8 +73,8 @@ mod api; mod builtin; mod engine; mod error; -mod fn_anonymous; mod fn_call; +mod fn_func; mod fn_register; mod optimize; mod parser; @@ -82,7 +92,7 @@ pub use result::EvalAltResult; pub use scope::Scope; #[cfg(not(feature = "no_function"))] -pub use fn_anonymous::AnonymousFn; +pub use fn_func::Func; #[cfg(not(feature = "no_index"))] pub use engine::Array; diff --git a/tests/call_fn.rs b/tests/call_fn.rs index 71df9489..82f42f93 100644 --- a/tests/call_fn.rs +++ b/tests/call_fn.rs @@ -1,5 +1,5 @@ #![cfg(not(feature = "no_function"))] -use rhai::{AnonymousFn, Engine, EvalAltResult, ParseErrorType, Scope, INT}; +use rhai::{Engine, EvalAltResult, Func, ParseErrorType, Scope, INT}; #[test] fn test_fn() -> Result<(), EvalAltResult> { @@ -62,7 +62,10 @@ fn test_call_fn() -> Result<(), EvalAltResult> { #[test] fn test_anonymous_fn() -> Result<(), EvalAltResult> { - let calc_func = AnonymousFn::<(INT, INT, INT), INT>::create_from_script( + let calc_func: Box Result> = + 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 }", "calc",