Implement function pointers.
This commit is contained in:
parent
aeb47efce8
commit
fe640e0e13
@ -9,6 +9,11 @@ Breaking changes
|
|||||||
|
|
||||||
* The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument.
|
* The trait function `ModuleResolver::resolve` no longer takes a `Scope` as argument.
|
||||||
|
|
||||||
|
New features
|
||||||
|
------------
|
||||||
|
|
||||||
|
* Support for _function pointers_ via `Fn(name)` and `Fn.call(...)` syntax - a poor man's first-class function.
|
||||||
|
|
||||||
Enhancements
|
Enhancements
|
||||||
------------
|
------------
|
||||||
|
|
||||||
|
@ -71,6 +71,7 @@ The Rhai Scripting Language
|
|||||||
14. [Functions](language/functions.md)
|
14. [Functions](language/functions.md)
|
||||||
1. [Function Overloading](language/overload.md)
|
1. [Function Overloading](language/overload.md)
|
||||||
2. [Call Method as Function](language/method.md)
|
2. [Call Method as Function](language/method.md)
|
||||||
|
3. [Function Pointers](language/fn-ptr.md)
|
||||||
15. [Print and Debug](language/print-debug.md)
|
15. [Print and Debug](language/print-debug.md)
|
||||||
16. [Modules](language/modules/index.md)
|
16. [Modules](language/modules/index.md)
|
||||||
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
1. [Export Variables, Functions and Sub-Modules](language/modules/export.md)
|
||||||
|
@ -35,6 +35,8 @@ Dynamic
|
|||||||
|
|
||||||
* Organize code base with dynamically-loadable [modules].
|
* Organize code base with dynamically-loadable [modules].
|
||||||
|
|
||||||
|
* [Function pointers].
|
||||||
|
|
||||||
Safe
|
Safe
|
||||||
----
|
----
|
||||||
|
|
||||||
|
@ -11,10 +11,13 @@ It doesn't attempt to be a new language. For example:
|
|||||||
* No traits... so it is also not Rust. Do your Rusty stuff in Rust.
|
* No traits... so it is also not Rust. Do your Rusty stuff in Rust.
|
||||||
|
|
||||||
* No structures/records - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
|
* No structures/records - define your types in Rust instead; Rhai can seamlessly work with _any Rust type_.
|
||||||
|
|
||||||
There is, however, a built-in [object map] type which is adequate for most uses.
|
There is, however, a built-in [object map] type which is adequate for most uses.
|
||||||
|
|
||||||
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
|
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
|
||||||
|
|
||||||
|
There is, however, support for simple [function pointers] allowing runtime dispatch by function name.
|
||||||
|
|
||||||
* No garbage collection - this should be expected, so...
|
* No garbage collection - this should be expected, so...
|
||||||
|
|
||||||
* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
* No closures - do your closure magic in Rust instead; [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
|
||||||
|
@ -27,6 +27,8 @@ if type_of(mystery) == "i64" {
|
|||||||
print("Hey, I got an array here!");
|
print("Hey, I got an array here!");
|
||||||
} else if type_of(mystery) == "map" {
|
} else if type_of(mystery) == "map" {
|
||||||
print("Hey, I got an object map here!");
|
print("Hey, I got an object map here!");
|
||||||
|
} else if type_of(mystery) == "Fn" {
|
||||||
|
print("Hey, I got a function pointer here!");
|
||||||
} else if type_of(mystery) == "TestStruct" {
|
} else if type_of(mystery) == "TestStruct" {
|
||||||
print("Hey, I got the TestStruct custom type here!");
|
print("Hey, I got the TestStruct custom type here!");
|
||||||
} else {
|
} else {
|
||||||
|
@ -28,7 +28,7 @@ eval("{ let z = y }"); // to keep a variable local, use a statement blo
|
|||||||
|
|
||||||
print("z = " + z); // <- error: variable 'z' not found
|
print("z = " + z); // <- error: variable 'z' not found
|
||||||
|
|
||||||
"print(42)".eval(); // <- nope... method-call style doesn't work
|
"print(42)".eval(); // <- nope... method-call style doesn't work with 'eval'
|
||||||
```
|
```
|
||||||
|
|
||||||
Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_,
|
Script segments passed to `eval` execute inside the current [`Scope`], so they can access and modify _everything_,
|
||||||
|
56
doc/src/language/fn-ptr.md
Normal file
56
doc/src/language/fn-ptr.md
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
Function Pointers
|
||||||
|
=================
|
||||||
|
|
||||||
|
It is possible to store a _function pointer_ in a variable just like a normal value.
|
||||||
|
In fact, internally a function pointer simply stores the _name_ of the function as a string.
|
||||||
|
|
||||||
|
Call a function pointer using the `call` method, which needs to be called in method-call style.
|
||||||
|
|
||||||
|
|
||||||
|
Built-in Functions
|
||||||
|
------------------
|
||||||
|
|
||||||
|
The following standard methods (mostly defined in the [`BasicFnPackage`]({{rootUrl}}/rust/packages.md) but excluded if
|
||||||
|
using a [raw `Engine`]) operate on [strings]:
|
||||||
|
|
||||||
|
| Function | Parameter(s) | Description |
|
||||||
|
| -------------------------- | ------------ | --------------------------------------------------------------------- |
|
||||||
|
| `name` method and property | _none_ | returns the name of the function encapsulated by the function pointer |
|
||||||
|
|
||||||
|
|
||||||
|
Examples
|
||||||
|
--------
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn foo(x) { 41 + x }
|
||||||
|
|
||||||
|
let func = Fn("foo"); // use the 'Fn' function to create a function pointer
|
||||||
|
|
||||||
|
print(func); // prints 'Fn(foo)'
|
||||||
|
|
||||||
|
let func = fn_name.Fn(); // <- error: 'Fn' cannot be called in method-call style
|
||||||
|
|
||||||
|
func.type_of() == "Fn"; // type_of() as function pointer is 'Fn'
|
||||||
|
|
||||||
|
func.name == "foo";
|
||||||
|
|
||||||
|
func.call(1) == 42; // call a function pointer with the 'call' method
|
||||||
|
|
||||||
|
foo(1) == 42; // <- the above de-sugars to this
|
||||||
|
|
||||||
|
call(func, 1); //<- error: 'call (Fn, i64)' is not a registered function
|
||||||
|
|
||||||
|
let len = Fn("len"); // 'Fn' also works with registered native Rust functions
|
||||||
|
|
||||||
|
len.call("hello") == 5;
|
||||||
|
|
||||||
|
let add = Fn("+"); // 'Fn' works with built-in operators also
|
||||||
|
|
||||||
|
add.call(40, 2) == 42;
|
||||||
|
|
||||||
|
let fn_name = "hello"; // the function name does not have to exist yet
|
||||||
|
|
||||||
|
let hello = Fn(fn_name + "_world");
|
||||||
|
|
||||||
|
hello.call(0); // error: function not found - "hello_world (i64)"
|
||||||
|
```
|
@ -6,16 +6,17 @@ Values and Types
|
|||||||
The following primitive types are supported natively:
|
The following primitive types are supported natively:
|
||||||
|
|
||||||
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
|
| Category | Equivalent Rust types | [`type_of()`] | `to_string()` |
|
||||||
| --------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | --------------------- |
|
| ----------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------- | --------------------- | ----------------------- |
|
||||||
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
|
| **Integer number** | `u8`, `i8`, `u16`, `i16`, <br/>`u32`, `i32` (default for [`only_i32`]),<br/>`u64`, `i64` _(default)_ | `"i32"`, `"u64"` etc. | `"42"`, `"123"` etc. |
|
||||||
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
| **Floating-point number** (disabled with [`no_float`]) | `f32`, `f64` _(default)_ | `"f32"` or `"f64"` | `"123.4567"` etc. |
|
||||||
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
|
| **Boolean value** | `bool` | `"bool"` | `"true"` or `"false"` |
|
||||||
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
|
| **Unicode character** | `char` | `"char"` | `"A"`, `"x"` etc. |
|
||||||
| **Immutable Unicode string** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
|
| **Immutable Unicode [string]** | `rhai::ImmutableString` (implemented as `Rc<String>` or `Arc<String>`) | `"string"` | `"hello"` etc. |
|
||||||
| **Array** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
|
| **[`Array`]** (disabled with [`no_index`]) | `rhai::Array` | `"array"` | `"[ ?, ?, ? ]"` |
|
||||||
| **Object map** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `#{ "a": 1, "b": 2 }` |
|
| **[Object map]** (disabled with [`no_object`]) | `rhai::Map` | `"map"` | `"#{ "a": 1, "b": 2 }"` |
|
||||||
| **Timestamp** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ |
|
| **[Timestamp]** (implemented in the [`BasicTimePackage`]({{rootUrl}}/rust/packages.md), disabled with [`no_std`]) | `std::time::Instant` ([`instant::Instant`](https://crates.io/crates/instant) if not [WASM] build) | `"timestamp"` | _not supported_ |
|
||||||
| **Dynamic value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
| **[Function pointer]** (disabled with [`no_function`]) | _None_ | `Fn` | `"Fn(foo)"` |
|
||||||
|
| **[`Dynamic`] value** (i.e. can be anything) | `rhai::Dynamic` | _the actual type_ | _actual value_ |
|
||||||
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
| **System integer** (current configuration) | `rhai::INT` (`i32` or `i64`) | `"i32"` or `"i64"` | `"42"`, `"123"` etc. |
|
||||||
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
| **System floating-point** (current configuration, disabled with [`no_float`]) | `rhai::FLOAT` (`f32` or `f64`) | `"f32"` or `"f64"` | `"123.456"` etc. |
|
||||||
| **Nothing/void/nil/null/Unit** (or whatever it is called) | `()` | `"()"` | `""` _(empty string)_ |
|
| **Nothing/void/nil/null/Unit** (or whatever it is called) | `()` | `"()"` | `""` _(empty string)_ |
|
||||||
|
@ -65,6 +65,8 @@
|
|||||||
|
|
||||||
[function]: {{rootUrl}}/language/functions.md
|
[function]: {{rootUrl}}/language/functions.md
|
||||||
[functions]: {{rootUrl}}/language/functions.md
|
[functions]: {{rootUrl}}/language/functions.md
|
||||||
|
[function pointer]: {{rootUrl}}/language/fn-ptr.md
|
||||||
|
[function pointers]: {{rootUrl}}/language/fn-ptr.md
|
||||||
|
|
||||||
[`Module`]: {{rootUrl}}/language/modules/index.md
|
[`Module`]: {{rootUrl}}/language/modules/index.md
|
||||||
[module]: {{rootUrl}}/language/modules/index.md
|
[module]: {{rootUrl}}/language/modules/index.md
|
||||||
|
@ -18,6 +18,7 @@ Built-In Packages
|
|||||||
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
| `BasicMathPackage` | Basic math functions (e.g. `sin`, `sqrt`) | No | Yes |
|
||||||
| `BasicArrayPackage` | Basic [array] functions (not available under `no_index`) | No | Yes |
|
| `BasicArrayPackage` | Basic [array] functions (not available under `no_index`) | No | Yes |
|
||||||
| `BasicMapPackage` | Basic [object map] functions (not available under `no_object`) | No | Yes |
|
| `BasicMapPackage` | Basic [object map] functions (not available under `no_object`) | No | Yes |
|
||||||
|
| `BasicFnPackage` | Basic methods for [function pointers]. | Yes | Yes |
|
||||||
| `EvalPackage` | Disable [`eval`] | No | No |
|
| `EvalPackage` | Disable [`eval`] | No | No |
|
||||||
| `CorePackage` | Basic essentials | Yes | Yes |
|
| `CorePackage` | Basic essentials | Yes | Yes |
|
||||||
| `StandardPackage` | Standard library (default for `Engine::new`) | No | Yes |
|
| `StandardPackage` | Standard library (default for `Engine::new`) | No | Yes |
|
||||||
|
40
src/any.rs
40
src/any.rs
@ -1,6 +1,6 @@
|
|||||||
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
//! Helper module which defines the `Any` trait to to allow dynamic value handling.
|
||||||
|
|
||||||
use crate::fn_native::SendSync;
|
use crate::fn_native::{FnPtr, SendSync};
|
||||||
use crate::module::Module;
|
use crate::module::Module;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
use crate::r#unsafe::{unsafe_cast_box, unsafe_try_cast};
|
||||||
@ -139,6 +139,8 @@ pub enum Union {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Map(Box<Map>),
|
Map(Box<Map>),
|
||||||
Module(Box<Module>),
|
Module(Box<Module>),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
FnPtr(FnPtr),
|
||||||
Variant(Box<Box<dyn Variant>>),
|
Variant(Box<Box<dyn Variant>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -176,6 +178,8 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => TypeId::of::<Map>(),
|
Union::Map(_) => TypeId::of::<Map>(),
|
||||||
Union::Module(_) => TypeId::of::<Module>(),
|
Union::Module(_) => TypeId::of::<Module>(),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(_) => TypeId::of::<FnPtr>(),
|
||||||
Union::Variant(value) => (***value).type_id(),
|
Union::Variant(value) => (***value).type_id(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -195,6 +199,8 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(_) => "map",
|
Union::Map(_) => "map",
|
||||||
Union::Module(_) => "sub-scope",
|
Union::Module(_) => "sub-scope",
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(_) => "Fn",
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -219,6 +225,8 @@ impl fmt::Display for Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
Union::Module(value) => fmt::Debug::fmt(value, f),
|
Union::Module(value) => fmt::Debug::fmt(value, f),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -243,6 +251,8 @@ impl fmt::Debug for Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => write!(f, "#{:?}", value),
|
Union::Map(value) => write!(f, "#{:?}", value),
|
||||||
Union::Module(value) => fmt::Debug::fmt(value, f),
|
Union::Module(value) => fmt::Debug::fmt(value, f),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(value) => fmt::Display::fmt(value, f),
|
||||||
|
|
||||||
#[cfg(not(feature = "no_std"))]
|
#[cfg(not(feature = "no_std"))]
|
||||||
#[cfg(not(target_arch = "wasm32"))]
|
#[cfg(not(target_arch = "wasm32"))]
|
||||||
@ -267,6 +277,8 @@ impl Clone for Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
Union::Map(ref value) => Self(Union::Map(value.clone())),
|
||||||
Union::Module(ref value) => Self(Union::Module(value.clone())),
|
Union::Module(ref value) => Self(Union::Module(value.clone())),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(ref value) => Self(Union::FnPtr(value.clone())),
|
||||||
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
Union::Variant(ref value) => (***value).clone_into_dynamic(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -396,6 +408,8 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Map(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
Union::Module(value) => unsafe_cast_box::<_, T>(value).ok().map(|v| *v),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(value) => unsafe_try_cast(value),
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).ok(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -439,6 +453,8 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
Union::Map(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
||||||
Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
Union::Module(value) => *unsafe_cast_box::<_, T>(value).unwrap(),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(value) => unsafe_try_cast(value).unwrap(),
|
||||||
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
|
Union::Variant(value) => (*value).as_box_any().downcast().map(|x| *x).unwrap(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -454,8 +470,7 @@ impl Dynamic {
|
|||||||
match &self.0 {
|
match &self.0 {
|
||||||
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Unit(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Bool(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
Union::Str(value) => (value as &dyn Any)
|
Union::Str(value) => <dyn Any>::downcast_ref::<T>(value)
|
||||||
.downcast_ref::<T>()
|
|
||||||
.or_else(|| <dyn Any>::downcast_ref::<T>(value.as_ref())),
|
.or_else(|| <dyn Any>::downcast_ref::<T>(value.as_ref())),
|
||||||
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Char(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
Union::Int(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
@ -466,6 +481,8 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Map(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
Union::Module(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
Union::Module(value) => <dyn Any>::downcast_ref::<T>(value.as_ref()),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(value) => <dyn Any>::downcast_ref::<T>(value),
|
||||||
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
Union::Variant(value) => value.as_ref().as_ref().as_any().downcast_ref::<T>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -491,6 +508,8 @@ impl Dynamic {
|
|||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Map(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
Union::Module(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
Union::Module(value) => <dyn Any>::downcast_mut::<T>(value.as_mut()),
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
Union::FnPtr(value) => <dyn Any>::downcast_mut::<T>(value),
|
||||||
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
Union::Variant(value) => value.as_mut().as_mut_any().downcast_mut::<T>(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -556,6 +575,15 @@ impl Dynamic {
|
|||||||
_ => Err(self.type_name()),
|
_ => Err(self.type_name()),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Cast the `Dynamic` as a `FnPtr` and return the function name.
|
||||||
|
/// Returns the name of the actual type if the cast fails.
|
||||||
|
pub(crate) fn as_fn_name(&self) -> Result<&str, &'static str> {
|
||||||
|
match &self.0 {
|
||||||
|
Union::FnPtr(f) => Ok(f.fn_name()),
|
||||||
|
_ => Err(self.type_name()),
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<()> for Dynamic {
|
impl From<()> for Dynamic {
|
||||||
@ -616,6 +644,12 @@ impl<K: Into<ImmutableString>, T: Variant + Clone> From<HashMap<K, T>> for Dynam
|
|||||||
)))
|
)))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
impl From<FnPtr> for Dynamic {
|
||||||
|
fn from(value: FnPtr) -> Self {
|
||||||
|
Self(Union::FnPtr(value))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only
|
/// Private type which ensures that `rhai::Any` and `rhai::AnyExt` can only
|
||||||
/// be implemented by this crate.
|
/// be implemented by this crate.
|
||||||
|
@ -3,7 +3,8 @@
|
|||||||
use crate::any::{Dynamic, Union, Variant};
|
use crate::any::{Dynamic, Union, Variant};
|
||||||
use crate::calc_fn_hash;
|
use crate::calc_fn_hash;
|
||||||
use crate::error::ParseErrorType;
|
use crate::error::ParseErrorType;
|
||||||
use crate::fn_native::{CallableFunction, Callback, FnCallArgs};
|
use crate::fn_native::{CallableFunction, Callback, FnCallArgs, FnPtr};
|
||||||
|
use crate::fn_register::RegisterResultFn;
|
||||||
use crate::module::{resolvers, Module, ModuleResolver};
|
use crate::module::{resolvers, Module, ModuleResolver};
|
||||||
use crate::optimize::OptimizationLevel;
|
use crate::optimize::OptimizationLevel;
|
||||||
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
|
use crate::packages::{Package, PackageLibrary, PackagesCollection, StandardPackage};
|
||||||
@ -11,7 +12,7 @@ use crate::parser::{Expr, FnAccess, ImmutableString, ReturnType, ScriptFnDef, St
|
|||||||
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
|
use crate::r#unsafe::{unsafe_cast_var_name_to_lifetime, unsafe_mut_cast_to_lifetime};
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
use crate::scope::{EntryType as ScopeEntryType, Scope};
|
||||||
use crate::token::Position;
|
use crate::token::{Position, Token};
|
||||||
use crate::utils::StaticVec;
|
use crate::utils::StaticVec;
|
||||||
|
|
||||||
#[cfg(not(feature = "no_float"))]
|
#[cfg(not(feature = "no_float"))]
|
||||||
@ -71,11 +72,13 @@ pub const KEYWORD_PRINT: &str = "print";
|
|||||||
pub const KEYWORD_DEBUG: &str = "debug";
|
pub const KEYWORD_DEBUG: &str = "debug";
|
||||||
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
pub const KEYWORD_TYPE_OF: &str = "type_of";
|
||||||
pub const KEYWORD_EVAL: &str = "eval";
|
pub const KEYWORD_EVAL: &str = "eval";
|
||||||
|
pub const KEYWORD_FN_PTR_CALL: &str = "call";
|
||||||
pub const FN_TO_STRING: &str = "to_string";
|
pub const FN_TO_STRING: &str = "to_string";
|
||||||
pub const FN_GET: &str = "get$";
|
pub const FN_GET: &str = "get$";
|
||||||
pub const FN_SET: &str = "set$";
|
pub const FN_SET: &str = "set$";
|
||||||
pub const FN_IDX_GET: &str = "$index$get$";
|
pub const FN_IDX_GET: &str = "$index$get$";
|
||||||
pub const FN_IDX_SET: &str = "$index$set$";
|
pub const FN_IDX_SET: &str = "$index$set$";
|
||||||
|
pub const FN_FN_PTR: &str = "Fn";
|
||||||
|
|
||||||
/// A type specifying the method of chaining.
|
/// A type specifying the method of chaining.
|
||||||
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash)]
|
||||||
@ -954,6 +957,14 @@ impl Engine {
|
|||||||
false,
|
false,
|
||||||
)),
|
)),
|
||||||
|
|
||||||
|
// Fn
|
||||||
|
FN_FN_PTR if args.len() == 1 && !self.has_override(lib, hashes) => {
|
||||||
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
|
"'Fn' should not be called in method style. Try Fn(...);".into(),
|
||||||
|
Position::none(),
|
||||||
|
)))
|
||||||
|
}
|
||||||
|
|
||||||
// eval - reaching this point it must be a method-style call
|
// eval - reaching this point it must be a method-style call
|
||||||
KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hashes) => {
|
KEYWORD_EVAL if args.len() == 1 && !self.has_override(lib, hashes) => {
|
||||||
Err(Box::new(EvalAltResult::ErrorRuntime(
|
Err(Box::new(EvalAltResult::ErrorRuntime(
|
||||||
@ -1124,18 +1135,31 @@ impl Engine {
|
|||||||
// Get a reference to the mutation target Dynamic
|
// Get a reference to the mutation target Dynamic
|
||||||
let (result, updated) = {
|
let (result, updated) = {
|
||||||
let obj = target.as_mut();
|
let obj = target.as_mut();
|
||||||
let mut arg_values: StaticVec<_> = once(obj)
|
let idx = idx_val.downcast_mut::<StaticVec<Dynamic>>().unwrap();
|
||||||
.chain(
|
|
||||||
idx_val
|
// Check if it is a FnPtr call
|
||||||
.downcast_mut::<StaticVec<Dynamic>>()
|
let (fn_name, mut arg_values, hash_fn, is_native) =
|
||||||
.unwrap()
|
if name == KEYWORD_FN_PTR_CALL && obj.is::<FnPtr>() {
|
||||||
.iter_mut(),
|
// Redirect function name
|
||||||
|
let name = obj.as_fn_name().unwrap();
|
||||||
|
// Recalculate hash
|
||||||
|
let hash = calc_fn_hash(empty(), name, idx.len(), empty());
|
||||||
|
// Arguments are passed as-is
|
||||||
|
(name, idx.iter_mut().collect::<StaticVec<_>>(), hash, false)
|
||||||
|
} else {
|
||||||
|
// Attached object pointer in front of the arguments
|
||||||
|
(
|
||||||
|
name.as_ref(),
|
||||||
|
once(obj).chain(idx.iter_mut()).collect::<StaticVec<_>>(),
|
||||||
|
*hash,
|
||||||
|
*native,
|
||||||
)
|
)
|
||||||
.collect();
|
};
|
||||||
|
|
||||||
let args = arg_values.as_mut();
|
let args = arg_values.as_mut();
|
||||||
|
|
||||||
self.exec_fn_call(
|
self.exec_fn_call(
|
||||||
state, lib, name, *native, *hash, args, is_ref, def_val, 0,
|
state, lib, fn_name, is_native, hash_fn, args, is_ref, def_val, 0,
|
||||||
)
|
)
|
||||||
.map_err(|err| EvalAltResult::new_position(err, *pos))?
|
.map_err(|err| EvalAltResult::new_position(err, *pos))?
|
||||||
};
|
};
|
||||||
@ -1697,6 +1721,27 @@ impl Engine {
|
|||||||
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
let ((name, native, pos), _, hash, args_expr, def_val) = x.as_ref();
|
||||||
let def_val = def_val.as_ref();
|
let def_val = def_val.as_ref();
|
||||||
|
|
||||||
|
// Handle Fn
|
||||||
|
if name == FN_FN_PTR && args_expr.len() == 1 {
|
||||||
|
let hash_fn =
|
||||||
|
calc_fn_hash(empty(), name, 1, once(TypeId::of::<ImmutableString>()));
|
||||||
|
|
||||||
|
if !self.has_override(lib, (hash_fn, *hash)) {
|
||||||
|
// Fn - only in function call style
|
||||||
|
let expr = args_expr.get(0);
|
||||||
|
let arg_value = self.eval_expr(scope, state, lib, expr, level)?;
|
||||||
|
return arg_value
|
||||||
|
.take_immutable_string()
|
||||||
|
.map(|s| FnPtr::from(s).into())
|
||||||
|
.map_err(|type_name| {
|
||||||
|
Box::new(EvalAltResult::ErrorMismatchOutputType(
|
||||||
|
type_name.into(),
|
||||||
|
Position::none(),
|
||||||
|
))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Handle eval
|
// Handle eval
|
||||||
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
if name == KEYWORD_EVAL && args_expr.len() == 1 {
|
||||||
let hash_fn =
|
let hash_fn =
|
||||||
|
@ -2,6 +2,7 @@ use crate::any::Dynamic;
|
|||||||
use crate::engine::Engine;
|
use crate::engine::Engine;
|
||||||
use crate::parser::ScriptFnDef;
|
use crate::parser::ScriptFnDef;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
use crate::stdlib::{boxed::Box, fmt, rc::Rc, sync::Arc};
|
use crate::stdlib::{boxed::Box, fmt, rc::Rc, sync::Arc};
|
||||||
|
|
||||||
@ -53,8 +54,37 @@ pub fn shared_take<T: Clone>(value: Shared<T>) -> T {
|
|||||||
|
|
||||||
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
pub type FnCallArgs<'a> = [&'a mut Dynamic];
|
||||||
|
|
||||||
|
/// A general function pointer.
|
||||||
|
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||||
|
pub struct FnPtr(ImmutableString);
|
||||||
|
|
||||||
|
impl FnPtr {
|
||||||
|
/// Get the name of the function.
|
||||||
|
pub fn fn_name(&self) -> &str {
|
||||||
|
self.get_fn_name().as_ref()
|
||||||
|
}
|
||||||
|
/// Get the name of the function.
|
||||||
|
pub(crate) fn get_fn_name(&self) -> &ImmutableString {
|
||||||
|
&self.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl fmt::Display for FnPtr {
|
||||||
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
|
write!(f, "Fn({})", self.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<S: Into<ImmutableString>> From<S> for FnPtr {
|
||||||
|
fn from(value: S) -> Self {
|
||||||
|
Self(value.into())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// A general function trail object.
|
||||||
#[cfg(not(feature = "sync"))]
|
#[cfg(not(feature = "sync"))]
|
||||||
pub type FnAny = dyn Fn(&Engine, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
pub type FnAny = dyn Fn(&Engine, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>>;
|
||||||
|
/// A general function trail object.
|
||||||
#[cfg(feature = "sync")]
|
#[cfg(feature = "sync")]
|
||||||
pub type FnAny =
|
pub type FnAny =
|
||||||
dyn Fn(&Engine, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
dyn Fn(&Engine, &mut FnCallArgs) -> Result<Dynamic, Box<EvalAltResult>> + Send + Sync;
|
||||||
|
8
src/packages/fn_basic.rs
Normal file
8
src/packages/fn_basic.rs
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
use crate::def_package;
|
||||||
|
use crate::fn_native::FnPtr;
|
||||||
|
|
||||||
|
def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, {
|
||||||
|
lib.set_fn_1_mut("name", |f: &mut FnPtr| Ok(f.get_fn_name().clone()));
|
||||||
|
lib.set_getter_fn("name", |f: &mut FnPtr| Ok(f.get_fn_name().clone()));
|
||||||
|
|
||||||
|
});
|
@ -9,6 +9,7 @@ use crate::stdlib::any::TypeId;
|
|||||||
pub(crate) mod arithmetic;
|
pub(crate) mod arithmetic;
|
||||||
mod array_basic;
|
mod array_basic;
|
||||||
mod eval;
|
mod eval;
|
||||||
|
mod fn_basic;
|
||||||
mod iter_basic;
|
mod iter_basic;
|
||||||
mod logic;
|
mod logic;
|
||||||
mod map_basic;
|
mod map_basic;
|
||||||
@ -23,6 +24,8 @@ pub use arithmetic::ArithmeticPackage;
|
|||||||
#[cfg(not(feature = "no_index"))]
|
#[cfg(not(feature = "no_index"))]
|
||||||
pub use array_basic::BasicArrayPackage;
|
pub use array_basic::BasicArrayPackage;
|
||||||
pub use eval::EvalPackage;
|
pub use eval::EvalPackage;
|
||||||
|
#[cfg(not(feature = "no_function"))]
|
||||||
|
pub use fn_basic::BasicFnPackage;
|
||||||
pub use iter_basic::BasicIteratorPackage;
|
pub use iter_basic::BasicIteratorPackage;
|
||||||
pub use logic::LogicPackage;
|
pub use logic::LogicPackage;
|
||||||
#[cfg(not(feature = "no_object"))]
|
#[cfg(not(feature = "no_object"))]
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
use super::arithmetic::ArithmeticPackage;
|
use super::arithmetic::ArithmeticPackage;
|
||||||
|
use super::fn_basic::BasicFnPackage;
|
||||||
use super::iter_basic::BasicIteratorPackage;
|
use super::iter_basic::BasicIteratorPackage;
|
||||||
use super::logic::LogicPackage;
|
use super::logic::LogicPackage;
|
||||||
use super::string_basic::BasicStringPackage;
|
use super::string_basic::BasicStringPackage;
|
||||||
@ -10,4 +11,5 @@ def_package!(crate:CorePackage:"_Core_ package containing basic facilities.", li
|
|||||||
LogicPackage::init(lib);
|
LogicPackage::init(lib);
|
||||||
BasicStringPackage::init(lib);
|
BasicStringPackage::init(lib);
|
||||||
BasicIteratorPackage::init(lib);
|
BasicIteratorPackage::init(lib);
|
||||||
|
BasicFnPackage::init(lib);
|
||||||
});
|
});
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
use crate::def_package;
|
use crate::def_package;
|
||||||
use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
use crate::engine::{FN_TO_STRING, KEYWORD_DEBUG, KEYWORD_PRINT};
|
||||||
|
use crate::fn_native::FnPtr;
|
||||||
use crate::module::FuncReturn;
|
use crate::module::FuncReturn;
|
||||||
use crate::parser::{ImmutableString, INT};
|
use crate::parser::{ImmutableString, INT};
|
||||||
|
|
||||||
@ -34,8 +35,9 @@ macro_rules! reg_op {
|
|||||||
}
|
}
|
||||||
|
|
||||||
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
def_package!(crate:BasicStringPackage:"Basic string utilities, including printing.", lib, {
|
||||||
reg_op!(lib, KEYWORD_PRINT, to_string, INT, bool, char);
|
reg_op!(lib, KEYWORD_PRINT, to_string, INT, bool, char, FnPtr);
|
||||||
reg_op!(lib, FN_TO_STRING, to_string, INT, bool, char);
|
reg_op!(lib, FN_TO_STRING, to_string, INT, bool, char, FnPtr);
|
||||||
|
lib.set_fn_1_mut(KEYWORD_DEBUG, |f: &mut FnPtr| Ok(f.to_string()));
|
||||||
|
|
||||||
lib.set_fn_0(KEYWORD_PRINT, || Ok("".to_string()));
|
lib.set_fn_0(KEYWORD_PRINT, || Ok("".to_string()));
|
||||||
lib.set_fn_1(KEYWORD_PRINT, |_: ()| Ok("".to_string()));
|
lib.set_fn_1(KEYWORD_PRINT, |_: ()| Ok("".to_string()));
|
||||||
|
@ -45,3 +45,44 @@ fn test_functions() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_function_pointers() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let engine = Engine::new();
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<String>(r#"type_of(Fn("abc"))"#)?, "Fn");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { 40 + x }
|
||||||
|
|
||||||
|
let fn_name = "f";
|
||||||
|
fn_name += "oo";
|
||||||
|
|
||||||
|
let f = Fn(fn_name);
|
||||||
|
f.call(2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.eval::<INT>(r#"let f = Fn("abc"); f.call(0)"#).expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(f, _) if f.starts_with("abc (")
|
||||||
|
));
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
engine.eval::<INT>(
|
||||||
|
r#"
|
||||||
|
fn foo(x) { 40 + x }
|
||||||
|
|
||||||
|
let x = #{ action: Fn("foo") };
|
||||||
|
x.action.call(2)
|
||||||
|
"#
|
||||||
|
)?,
|
||||||
|
42
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user