Merge branch 'master' into plugins
This commit is contained in:
commit
d4ebb3c96d
44
README.md
44
README.md
@ -684,13 +684,18 @@ Rhai's scripting engine is very lightweight. It gets most of its abilities from
|
|||||||
To call these functions, they need to be registered with the [`Engine`].
|
To call these functions, they need to be registered with the [`Engine`].
|
||||||
|
|
||||||
```rust
|
```rust
|
||||||
use rhai::{Dynamic, Engine, EvalAltResult};
|
use rhai::{Dynamic, Engine, EvalAltResult, ImmutableString};
|
||||||
use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn'
|
use rhai::RegisterFn; // use 'RegisterFn' trait for 'register_fn'
|
||||||
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
use rhai::RegisterResultFn; // use 'RegisterResultFn' trait for 'register_result_fn'
|
||||||
|
|
||||||
// Normal function that returns any value type
|
// Normal function that returns a standard type
|
||||||
fn add(x: i64, y: i64) -> i64 {
|
// Remember to use 'ImmutableString' and not 'String'
|
||||||
x + y
|
fn add_len(x: i64, s: ImmutableString) -> i64 {
|
||||||
|
x + s.len()
|
||||||
|
}
|
||||||
|
// Alternatively, '&str' maps directly to 'ImmutableString'
|
||||||
|
fn add_len_str(x: i64, s: &str) -> i64 {
|
||||||
|
x + s.len()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Function that returns a 'Dynamic' value - must return a 'Result'
|
// Function that returns a 'Dynamic' value - must return a 'Result'
|
||||||
@ -702,9 +707,14 @@ fn main() -> Result<(), Box<EvalAltResult>>
|
|||||||
{
|
{
|
||||||
let engine = Engine::new();
|
let engine = Engine::new();
|
||||||
|
|
||||||
engine.register_fn("add", add);
|
engine.register_fn("add", add_len);
|
||||||
|
engine.register_fn("add_str", add_len_str);
|
||||||
|
|
||||||
let result = engine.eval::<i64>("add(40, 2)")?;
|
let result = engine.eval::<i64>(r#"add(40, "xx")"#)?;
|
||||||
|
|
||||||
|
println!("Answer: {}", result); // prints 42
|
||||||
|
|
||||||
|
let result = engine.eval::<i64>(r#"add_str(40, "xx")"#)?;
|
||||||
|
|
||||||
println!("Answer: {}", result); // prints 42
|
println!("Answer: {}", result); // prints 42
|
||||||
|
|
||||||
@ -735,6 +745,25 @@ i.e. different functions can have the same name as long as their parameters are
|
|||||||
and/or different number.
|
and/or different number.
|
||||||
New definitions _overwrite_ previous definitions of the same name and same number/types of parameters.
|
New definitions _overwrite_ previous definitions of the same name and same number/types of parameters.
|
||||||
|
|
||||||
|
### `String` parameters
|
||||||
|
|
||||||
|
Functions accepting a parameter of `String` should use `&str` instead because it maps directly to `ImmutableString`
|
||||||
|
which is the type that Rhai uses to represent strings internally.
|
||||||
|
|
||||||
|
```rust
|
||||||
|
fn get_len1(s: String) -> i64 { s.len() as i64 } // <- Rhai will not find this function
|
||||||
|
fn get_len2(s: &str) -> i64 { s.len() as i64 } // <- Rhai finds this function fine
|
||||||
|
fn get_len3(s: ImmutableString) -> i64 { s.len() as i64 } // <- the above is equivalent to this
|
||||||
|
|
||||||
|
engine.register_fn("len1", get_len1);
|
||||||
|
engine.register_fn("len2", get_len2);
|
||||||
|
engine.register_fn("len3", get_len3);
|
||||||
|
|
||||||
|
let len = engine.eval::<i64>("x.len1()")?; // error: function 'len1 (string)' not found
|
||||||
|
let len = engine.eval::<i64>("x.len2()")?; // works fine
|
||||||
|
let len = engine.eval::<i64>("x.len3()")?; // works fine
|
||||||
|
```
|
||||||
|
|
||||||
Generic functions
|
Generic functions
|
||||||
-----------------
|
-----------------
|
||||||
|
|
||||||
@ -1388,6 +1417,9 @@ Strings and Chars
|
|||||||
[strings]: #strings-and-chars
|
[strings]: #strings-and-chars
|
||||||
[char]: #strings-and-chars
|
[char]: #strings-and-chars
|
||||||
|
|
||||||
|
All strings in Rhai are implemented as `ImmutableString` (see [standard types]).
|
||||||
|
`ImmutableString` should be used in place of the standard Rust type `String` when registering functions.
|
||||||
|
|
||||||
String and character literals follow C-style formatting, with support for Unicode ('`\u`_xxxx_' or '`\U`_xxxxxxxx_')
|
String and character literals follow C-style formatting, with support for Unicode ('`\u`_xxxx_' or '`\U`_xxxxxxxx_')
|
||||||
and hex ('`\x`_xx_') escape sequences.
|
and hex ('`\x`_xx_') escape sequences.
|
||||||
|
|
||||||
|
@ -15,6 +15,7 @@ New features
|
|||||||
------------
|
------------
|
||||||
|
|
||||||
* Indexers are now split into getters ans setters (which now support updates). The API is split into `Engine::register_indexer_get` and `Engine::register_indexer_set` with `Engine::register_indexer_get_set` being a shorthand. Similarly, `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn` are added.
|
* Indexers are now split into getters ans setters (which now support updates). The API is split into `Engine::register_indexer_get` and `Engine::register_indexer_set` with `Engine::register_indexer_get_set` being a shorthand. Similarly, `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn` are added.
|
||||||
|
* `Engine:register_fn` and `Engine:register_result_fn` accepts functions that take parameters of type `&str` (immutable string slice), which maps directly to `ImmutableString`. This is to avoid needing wrappers for functions taking string parameters.
|
||||||
|
|
||||||
|
|
||||||
Version 0.15.0
|
Version 0.15.0
|
||||||
|
@ -708,7 +708,11 @@ impl Engine {
|
|||||||
"{} ({})",
|
"{} ({})",
|
||||||
fn_name,
|
fn_name,
|
||||||
args.iter()
|
args.iter()
|
||||||
.map(|name| self.map_type_name(name.type_name()))
|
.map(|name| if name.is::<ImmutableString>() {
|
||||||
|
"&str | ImmutableString"
|
||||||
|
} else {
|
||||||
|
self.map_type_name(name.type_name())
|
||||||
|
})
|
||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(", ")
|
.join(", ")
|
||||||
),
|
),
|
||||||
@ -906,6 +910,9 @@ impl Engine {
|
|||||||
let mut idx_val = idx_values.pop();
|
let mut idx_val = idx_values.pop();
|
||||||
|
|
||||||
if is_index {
|
if is_index {
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
unreachable!();
|
||||||
|
|
||||||
let pos = rhs.position();
|
let pos = rhs.position();
|
||||||
|
|
||||||
match rhs {
|
match rhs {
|
||||||
@ -1292,6 +1299,7 @@ impl Engine {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(not(feature = "no_index"))]
|
||||||
_ => {
|
_ => {
|
||||||
let fn_name = FUNC_INDEXER_GET;
|
let fn_name = FUNC_INDEXER_GET;
|
||||||
let type_name = self.map_type_name(val.type_name());
|
let type_name = self.map_type_name(val.type_name());
|
||||||
@ -1305,6 +1313,12 @@ impl Engine {
|
|||||||
))
|
))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "no_index")]
|
||||||
|
_ => Err(Box::new(EvalAltResult::ErrorIndexingType(
|
||||||
|
self.map_type_name(val.type_name()).into(),
|
||||||
|
Position::none(),
|
||||||
|
))),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use crate::fn_native::{CallableFunction, FnAny, FnCallArgs};
|
|||||||
use crate::parser::FnAccess;
|
use crate::parser::FnAccess;
|
||||||
use crate::plugin::Plugin;
|
use crate::plugin::Plugin;
|
||||||
use crate::result::EvalAltResult;
|
use crate::result::EvalAltResult;
|
||||||
|
use crate::utils::ImmutableString;
|
||||||
|
|
||||||
use crate::stdlib::{any::TypeId, boxed::Box, mem};
|
use crate::stdlib::{any::TypeId, boxed::Box, mem};
|
||||||
|
|
||||||
@ -179,10 +180,17 @@ pub fn by_ref<T: Variant + Clone>(data: &mut Dynamic) -> &mut T {
|
|||||||
/// Dereference into value.
|
/// Dereference into value.
|
||||||
#[inline(always)]
|
#[inline(always)]
|
||||||
pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
|
||||||
|
if TypeId::of::<T>() == TypeId::of::<&str>() {
|
||||||
|
// If T is &str, data must be ImmutableString, so map directly to it
|
||||||
|
let ref_str = data.as_str().unwrap();
|
||||||
|
let ref_T = unsafe { mem::transmute::<_, &T>(&ref_str) };
|
||||||
|
ref_T.clone()
|
||||||
|
} else {
|
||||||
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
// We consume the argument and then replace it with () - the argument is not supposed to be used again.
|
||||||
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
// This way, we avoid having to clone the argument again, because it is already a clone when passed here.
|
||||||
mem::take(data).cast::<T>()
|
mem::take(data).cast::<T>()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<PL: Plugin> RegisterPlugin<PL> for Engine {
|
impl<PL: Plugin> RegisterPlugin<PL> for Engine {
|
||||||
fn register_plugin(&mut self, plugin: PL) {
|
fn register_plugin(&mut self, plugin: PL) {
|
||||||
@ -232,6 +240,18 @@ pub fn map_result(
|
|||||||
data
|
data
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Remap `&str` to `ImmutableString`.
|
||||||
|
#[inline(always)]
|
||||||
|
fn map_type_id<T: 'static>() -> TypeId {
|
||||||
|
let id = TypeId::of::<T>();
|
||||||
|
|
||||||
|
if id == TypeId::of::<&str>() {
|
||||||
|
TypeId::of::<ImmutableString>()
|
||||||
|
} else {
|
||||||
|
id
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
macro_rules! def_register {
|
macro_rules! def_register {
|
||||||
() => {
|
() => {
|
||||||
def_register!(imp from_pure :);
|
def_register!(imp from_pure :);
|
||||||
@ -256,7 +276,7 @@ macro_rules! def_register {
|
|||||||
{
|
{
|
||||||
fn register_fn(&mut self, name: &str, f: FN) {
|
fn register_fn(&mut self, name: &str, f: FN) {
|
||||||
self.global_module.set_fn(name, FnAccess::Public,
|
self.global_module.set_fn(name, FnAccess::Public,
|
||||||
&[$(TypeId::of::<$par>()),*],
|
&[$(map_type_id::<$par>()),*],
|
||||||
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $clone),*))
|
CallableFunction::$abi(make_func!(f : map_dynamic ; $($par => $clone),*))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
@ -273,7 +293,7 @@ macro_rules! def_register {
|
|||||||
{
|
{
|
||||||
fn register_result_fn(&mut self, name: &str, f: FN) {
|
fn register_result_fn(&mut self, name: &str, f: FN) {
|
||||||
self.global_module.set_fn(name, FnAccess::Public,
|
self.global_module.set_fn(name, FnAccess::Public,
|
||||||
&[$(TypeId::of::<$par>()),*],
|
&[$(map_type_id::<$par>()),*],
|
||||||
CallableFunction::$abi(make_func!(f : map_result ; $($par => $clone),*))
|
CallableFunction::$abi(make_func!(f : map_result ; $($par => $clone),*))
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
17
src/utils.rs
17
src/utils.rs
@ -190,8 +190,9 @@ impl<T: Clone> Clone for StaticVec<T> {
|
|||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
for x in 0..self.len {
|
for x in 0..self.len {
|
||||||
let item: &T = unsafe { mem::transmute(self.list.get(x).unwrap()) };
|
let item = self.list.get(x).unwrap();
|
||||||
value.list[x] = MaybeUninit::new(item.clone());
|
let item_value = unsafe { mem::transmute::<_, &T>(item) };
|
||||||
|
value.list[x] = MaybeUninit::new(item_value.clone());
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
value.more = self.more.clone();
|
value.more = self.more.clone();
|
||||||
@ -424,7 +425,7 @@ impl<T> StaticVec<T> {
|
|||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
let list = unsafe { mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list) };
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
list.get(index).unwrap()
|
list.get(index).unwrap()
|
||||||
@ -442,7 +443,7 @@ impl<T> StaticVec<T> {
|
|||||||
panic!("index OOB in StaticVec");
|
panic!("index OOB in StaticVec");
|
||||||
}
|
}
|
||||||
|
|
||||||
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
let list = unsafe { mem::transmute::<_, &mut [T; MAX_STATIC_VEC]>(&mut self.list) };
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
list.get_mut(index).unwrap()
|
list.get_mut(index).unwrap()
|
||||||
@ -452,7 +453,7 @@ impl<T> StaticVec<T> {
|
|||||||
}
|
}
|
||||||
/// Get an iterator to entries in the `StaticVec`.
|
/// Get an iterator to entries in the `StaticVec`.
|
||||||
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
pub fn iter(&self) -> impl Iterator<Item = &T> {
|
||||||
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
let list = unsafe { mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list) };
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
list[..self.len].iter()
|
list[..self.len].iter()
|
||||||
@ -462,7 +463,7 @@ impl<T> StaticVec<T> {
|
|||||||
}
|
}
|
||||||
/// Get a mutable iterator to entries in the `StaticVec`.
|
/// Get a mutable iterator to entries in the `StaticVec`.
|
||||||
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
pub fn iter_mut(&mut self) -> impl Iterator<Item = &mut T> {
|
||||||
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
let list = unsafe { mem::transmute::<_, &mut [T; MAX_STATIC_VEC]>(&mut self.list) };
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
list[..self.len].iter_mut()
|
list[..self.len].iter_mut()
|
||||||
@ -549,7 +550,7 @@ impl<T: fmt::Debug> fmt::Debug for StaticVec<T> {
|
|||||||
|
|
||||||
impl<T> AsRef<[T]> for StaticVec<T> {
|
impl<T> AsRef<[T]> for StaticVec<T> {
|
||||||
fn as_ref(&self) -> &[T] {
|
fn as_ref(&self) -> &[T] {
|
||||||
let list: &[T; MAX_STATIC_VEC] = unsafe { mem::transmute(&self.list) };
|
let list = unsafe { mem::transmute::<_, &[T; MAX_STATIC_VEC]>(&self.list) };
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
&list[..self.len]
|
&list[..self.len]
|
||||||
@ -561,7 +562,7 @@ impl<T> AsRef<[T]> for StaticVec<T> {
|
|||||||
|
|
||||||
impl<T> AsMut<[T]> for StaticVec<T> {
|
impl<T> AsMut<[T]> for StaticVec<T> {
|
||||||
fn as_mut(&mut self) -> &mut [T] {
|
fn as_mut(&mut self) -> &mut [T] {
|
||||||
let list: &mut [T; MAX_STATIC_VEC] = unsafe { mem::transmute(&mut self.list) };
|
let list = unsafe { mem::transmute::<_, &mut [T; MAX_STATIC_VEC]>(&mut self.list) };
|
||||||
|
|
||||||
if self.is_fixed_storage() {
|
if self.is_fixed_storage() {
|
||||||
&mut list[..self.len]
|
&mut list[..self.len]
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
use rhai::{Engine, EvalAltResult, INT};
|
use rhai::{Engine, EvalAltResult, ImmutableString, RegisterFn, INT};
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_string() -> Result<(), Box<EvalAltResult>> {
|
fn test_string() -> Result<(), Box<EvalAltResult>> {
|
||||||
@ -154,3 +154,22 @@ fn test_string_substring() -> Result<(), Box<EvalAltResult>> {
|
|||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_fn() -> Result<(), Box<EvalAltResult>> {
|
||||||
|
let mut engine = Engine::new();
|
||||||
|
|
||||||
|
engine.register_fn("foo1", |s: &str| s.len() as INT);
|
||||||
|
engine.register_fn("foo2", |s: ImmutableString| s.len() as INT);
|
||||||
|
engine.register_fn("foo3", |s: String| s.len() as INT);
|
||||||
|
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"foo1("hello")"#)?, 5);
|
||||||
|
assert_eq!(engine.eval::<INT>(r#"foo2("hello")"#)?, 5);
|
||||||
|
|
||||||
|
assert!(matches!(
|
||||||
|
*engine.eval::<INT>(r#"foo3("hello")"#).expect_err("should error"),
|
||||||
|
EvalAltResult::ErrorFunctionNotFound(ref x, _) if x == "foo3 (&str | ImmutableString)"
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user