Merge pull request #249 from schungx/master

Finalize 0.19.0.
This commit is contained in:
Stephen Chung 2020-10-01 10:43:13 +08:00 committed by GitHub
commit fca908ef58
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
47 changed files with 644 additions and 449 deletions

View File

@ -6,11 +6,11 @@ members = [
[package]
name = "rhai"
version = "0.18.3"
version = "0.19.0"
edition = "2018"
authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"]
description = "Embedded scripting for Rust"
homepage = "https://github.com/jonathandturner/rhai"
homepage = "https://schungx.github.io/rhai"
repository = "https://github.com/jonathandturner/rhai"
readme = "README.md"
license = "MIT OR Apache-2.0"
@ -27,7 +27,6 @@ smallvec = { version = "1.4.2", default-features = false }
rhai_codegen = { version = "0.1", path = "codegen" }
[features]
#default = ["unchecked", "sync", "no_optimize", "no_float", "only_i32", "no_index", "no_object", "no_function", "no_module"]
default = []
unchecked = [] # unchecked arithmetic
sync = [] # restrict to only types that implement Send + Sync

View File

@ -4,7 +4,7 @@ version = "0.1.0"
edition = "2018"
authors = ["jhwgh1968"]
description = "Proceducral macro support package for Rhai, a scripting language for Rust"
homepage = "https://github.com/jonathandturner/rhai"
homepage = "https://schungx.github.io/rhai/plugins/index.html"
repository = "https://github.com/jonathandturner/rhai"
license = "MIT OR Apache-2.0"

View File

@ -580,7 +580,7 @@ impl ExportedFn {
);
quote! {
pub fn #callable_fn_name() -> CallableFunction {
CallableFunction::from_plugin(#token_name())
#token_name().into()
}
}
}
@ -628,7 +628,7 @@ impl ExportedFn {
arg_type.span()=> &mut args[0usize].write_lock::<#arg_type>().unwrap());
unpack_stmts.push(
syn::parse2::<syn::Stmt>(quote! {
let #var: &mut _ = #downcast_span;
let #var = #downcast_span;
})
.unwrap(),
);
@ -757,7 +757,7 @@ impl ExportedFn {
}
fn is_method_call(&self) -> bool { #is_method_call }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(#type_name()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![#(#input_type_exprs),*].into_boxed_slice()

View File

@ -1,17 +1,14 @@
//! This crate contains procedural macros to make creating Rhai plugin-modules much easier.
//!
//! This crate contains procedural macros to make creating Rhai modules much easier.
//!
//! # Exporting a Macro to Rhai
//! # Export an Entire Rust Module to a Rhai `Module`
//!
//! ```
//! use rhai::{EvalAltResult, FLOAT};
//! use rhai::plugin::*;
//! use rhai::module_resolvers::*;
//!
//! #[rhai::export_module]
//! pub mod advanced_math {
//! use rhai::FLOAT;
//!
//! #[export_module]
//! mod advanced_math {
//! pub const MYSTIC_NUMBER: FLOAT = 42.0 as FLOAT;
//!
//! pub fn euclidean_distance(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
@ -35,15 +32,15 @@
//! }
//! ```
//!
//! # Exporting a Function to a Rhai Module
//! # Register a Rust Function with a Rhai `Module`
//!
//! ```
//! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn};
//! use rhai::plugin::*;
//! use rhai::module_resolvers::*;
//!
//! #[rhai::export_fn]
//! pub fn distance_function(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
//! #[export_fn]
//! fn distance_function(x1: FLOAT, y1: FLOAT, x2: FLOAT, y2: FLOAT) -> FLOAT {
//! ((y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0)).sqrt()
//! }
//!
@ -66,7 +63,7 @@
//! }
//! ```
//!
//! # Exporting a Function to an Engine
//! # Register a Plugin Function with an `Engine`
//!
//! ```
//! use rhai::{EvalAltResult, FLOAT, Module, RegisterFn};
@ -105,6 +102,18 @@ mod rhai_module;
#[cfg(test)]
mod test;
/// Attribute, when put on a Rust function, turns it into a _plugin function_.
///
/// # Usage
///
/// ```,ignore
/// use rhai::plugin::*;
///
/// #[export_fn]
/// fn my_plugin_function(...) {
/// ...
/// }
/// ```
#[proc_macro_attribute]
pub fn export_fn(
args: proc_macro::TokenStream,
@ -125,6 +134,18 @@ pub fn export_fn(
proc_macro::TokenStream::from(output)
}
/// Attribute, when put on a Rust module, turns it into a _plugin module_.
///
/// # Usage
///
/// ```,ignore
/// use rhai::plugin::*;
///
/// #[export_module]
/// mod my_plugin_module {
/// ...
/// }
/// ```
#[proc_macro_attribute]
pub fn export_module(
args: proc_macro::TokenStream,
@ -143,6 +164,20 @@ pub fn export_module(
proc_macro::TokenStream::from(tokens)
}
/// Macro to generate a Rhai `Module` from a _plugin module_.
///
/// # Usage
///
/// ```,ignore
/// use rhai::plugin::*;
///
/// #[export_module]
/// mod my_plugin_module {
/// ...
/// }
///
/// let module = exported_module!(my_plugin_module);
/// ```
#[proc_macro]
pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::TokenStream {
let module_path = parse_macro_input!(module_path as syn::Path);
@ -152,6 +187,34 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke
proc_macro::TokenStream::from(tokens)
}
/// Macro to combine a _plugin module_ into an existing module.
///
/// Functions and variables in the plugin module overrides any existing similarly-named
/// functions and variables in the target module.
///
/// This call is intended to be used within the `def_package!` macro to define a custom
/// package based on a plugin module.
///
/// All sub-modules, if any, in the plugin module are _flattened_ and their functions/variables
/// registered at the top level because packages require so.
///
/// The text string name in the second parameter can be anything and is reserved for future use;
/// it is recommended to be an ID string that uniquely identifies the plugin module.
///
/// # Usage
///
/// ```,ignore
/// use rhai::plugin::*;
///
/// #[export_module]
/// mod my_plugin_module {
/// ...
/// }
///
/// let mut module = Module::new();
///
/// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module);
/// ```
#[proc_macro]
pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (module_expr, _export_name, module_path) = match crate::register::parse_register_macro(args)
@ -165,6 +228,22 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro
proc_macro::TokenStream::from(tokens)
}
/// Macro to register a _plugin function_ into an `Engine`.
///
/// # Usage
///
/// ```,ignore
/// use rhai::plugin::*;
///
/// #[export_fn]
/// fn my_plugin_function(...) {
/// ...
/// }
///
/// let mut engine = Engine::new();
///
/// register_exported_fn!(engine, "calc", my_plugin_function);
/// ```
#[proc_macro]
pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (engine_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args)
@ -179,6 +258,22 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS
proc_macro::TokenStream::from(tokens)
}
/// Macro to register a _plugin function_ into a Rhai `Module`.
///
/// # Usage
///
/// ```,ignore
/// use rhai::plugin::*;
///
/// #[export_fn]
/// fn my_plugin_function(...) {
/// ...
/// }
///
/// let mut module = Module::new();
///
/// set_exported_fn!(module, "calc", my_plugin_function);
/// ```
#[proc_macro]
pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream {
let (module_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args)

View File

@ -126,7 +126,7 @@ pub(crate) fn generate_body(
set_fn_stmts.push(
syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*],
CallableFunction::from_plugin(#fn_token_name()));
#fn_token_name().into());
})
.unwrap(),
);

View File

@ -283,14 +283,14 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![].into_boxed_slice()
}
}
pub fn token_callable() -> CallableFunction {
CallableFunction::from_plugin(Token())
Token().into()
}
pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
@ -328,14 +328,14 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>()].into_boxed_slice()
}
}
pub fn token_callable() -> CallableFunction {
CallableFunction::from_plugin(Token())
Token().into()
}
pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
@ -369,7 +369,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(MyType()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>()].into_boxed_slice()
@ -404,7 +404,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>(),
@ -412,7 +412,7 @@ mod generate_tests {
}
}
pub fn token_callable() -> CallableFunction {
CallableFunction::from_plugin(Token())
Token().into()
}
pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
@ -446,12 +446,12 @@ mod generate_tests {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
let arg1 = mem::take(args[1usize]).cast::<usize>();
let arg0: &mut _ = &mut args[0usize].write_lock::<usize>().unwrap();
let arg0 = &mut args[0usize].write_lock::<usize>().unwrap();
Ok(Dynamic::from(increment(arg0, arg1)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<usize>(),
@ -459,7 +459,7 @@ mod generate_tests {
}
}
pub fn token_callable() -> CallableFunction {
CallableFunction::from_plugin(Token())
Token().into()
}
pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()
@ -498,14 +498,14 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> { Box::new(Token()) }
fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<ImmutableString>()].into_boxed_slice()
}
}
pub fn token_callable() -> CallableFunction {
CallableFunction::from_plugin(Token())
Token().into()
}
pub fn token_input_types() -> Box<[TypeId]> {
Token().input_types()

View File

@ -296,7 +296,7 @@ mod generate_tests {
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("get_mystic_number", FnAccess::Public, &[],
CallableFunction::from_plugin(get_mystic_number_token()));
get_mystic_number_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -311,7 +311,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(get_mystic_number_token())
}
@ -320,7 +320,7 @@ mod generate_tests {
}
}
pub fn get_mystic_number_token_callable() -> CallableFunction {
CallableFunction::from_plugin(get_mystic_number_token())
get_mystic_number_token().into()
}
pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> {
get_mystic_number_token().input_types()
@ -358,7 +358,7 @@ mod generate_tests {
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("add_one_to", FnAccess::Public, &[core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_one_to_token()));
add_one_to_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -374,7 +374,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(add_one_to_token())
}
@ -383,7 +383,7 @@ mod generate_tests {
}
}
pub fn add_one_to_token_callable() -> CallableFunction {
CallableFunction::from_plugin(add_one_to_token())
add_one_to_token().into()
}
pub fn add_one_to_token_input_types() -> Box<[TypeId]> {
add_one_to_token().input_types()
@ -432,10 +432,10 @@ mod generate_tests {
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_one_to_token()));
add_one_to_token().into());
m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_n_to_token()));
add_n_to_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -451,7 +451,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(add_one_to_token())
}
@ -460,7 +460,7 @@ mod generate_tests {
}
}
pub fn add_one_to_token_callable() -> CallableFunction {
CallableFunction::from_plugin(add_one_to_token())
add_one_to_token().into()
}
pub fn add_one_to_token_input_types() -> Box<[TypeId]> {
add_one_to_token().input_types()
@ -480,7 +480,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(add_n_to_token())
}
@ -490,7 +490,7 @@ mod generate_tests {
}
}
pub fn add_n_to_token_callable() -> CallableFunction {
CallableFunction::from_plugin(add_n_to_token())
add_n_to_token().into()
}
pub fn add_n_to_token_input_types() -> Box<[TypeId]> {
add_n_to_token().input_types()
@ -529,7 +529,7 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_together_token()));
add_together_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -546,7 +546,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(add_together_token())
}
@ -556,7 +556,7 @@ mod generate_tests {
}
}
pub fn add_together_token_callable() -> CallableFunction {
CallableFunction::from_plugin(add_together_token())
add_together_token().into()
}
pub fn add_together_token_input_types() -> Box<[TypeId]> {
add_together_token().input_types()
@ -596,13 +596,13 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("add", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_together_token()));
add_together_token().into());
m.set_fn("+", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_together_token()));
add_together_token().into());
m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
CallableFunction::from_plugin(add_together_token()));
add_together_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -619,7 +619,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(add_together_token())
}
@ -629,7 +629,7 @@ mod generate_tests {
}
}
pub fn add_together_token_callable() -> CallableFunction {
CallableFunction::from_plugin(add_together_token())
add_together_token().into()
}
pub fn add_together_token_input_types() -> Box<[TypeId]> {
add_together_token().input_types()
@ -844,7 +844,7 @@ mod generate_tests {
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("get_mystic_number", FnAccess::Public, &[],
CallableFunction::from_plugin(get_mystic_number_token()));
get_mystic_number_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -859,7 +859,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(get_mystic_number_token())
}
@ -868,7 +868,7 @@ mod generate_tests {
}
}
pub fn get_mystic_number_token_callable() -> CallableFunction {
CallableFunction::from_plugin(get_mystic_number_token())
get_mystic_number_token().into()
}
pub fn get_mystic_number_token_input_types() -> Box<[TypeId]> {
get_mystic_number_token().input_types()
@ -937,7 +937,7 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("print_out_to", FnAccess::Public,
&[core::any::TypeId::of::<ImmutableString>()],
CallableFunction::from_plugin(print_out_to_token()));
print_out_to_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -953,7 +953,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(print_out_to_token())
}
@ -962,7 +962,7 @@ mod generate_tests {
}
}
pub fn print_out_to_token_callable() -> CallableFunction {
CallableFunction::from_plugin(print_out_to_token())
print_out_to_token().into()
}
pub fn print_out_to_token_input_types() -> Box<[TypeId]> {
print_out_to_token().input_types()
@ -1001,7 +1001,7 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("print_out_to", FnAccess::Public,
&[core::any::TypeId::of::<ImmutableString>()],
CallableFunction::from_plugin(print_out_to_token()));
print_out_to_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1017,7 +1017,7 @@ mod generate_tests {
}
fn is_method_call(&self) -> bool { false }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(print_out_to_token())
}
@ -1026,7 +1026,7 @@ mod generate_tests {
}
}
pub fn print_out_to_token_callable() -> CallableFunction {
CallableFunction::from_plugin(print_out_to_token())
print_out_to_token().into()
}
pub fn print_out_to_token_input_types() -> Box<[TypeId]> {
print_out_to_token().input_types()
@ -1065,7 +1065,7 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("increment", FnAccess::Public,
&[core::any::TypeId::of::<FLOAT>()],
CallableFunction::from_plugin(increment_token()));
increment_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1076,12 +1076,12 @@ mod generate_tests {
) -> Result<Dynamic, Box<EvalAltResult>> {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0: &mut _ = &mut args[0usize].write_lock::<FLOAT>().unwrap();
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
Ok(Dynamic::from(increment(arg0)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(increment_token())
}
@ -1090,7 +1090,7 @@ mod generate_tests {
}
}
pub fn increment_token_callable() -> CallableFunction {
CallableFunction::from_plugin(increment_token())
increment_token().into()
}
pub fn increment_token_input_types() -> Box<[TypeId]> {
increment_token().input_types()
@ -1132,7 +1132,7 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("increment", FnAccess::Public,
&[core::any::TypeId::of::<FLOAT>()],
CallableFunction::from_plugin(increment_token()));
increment_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1143,12 +1143,12 @@ mod generate_tests {
) -> Result<Dynamic, Box<EvalAltResult>> {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0: &mut _ = &mut args[0usize].write_lock::<FLOAT>().unwrap();
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
Ok(Dynamic::from(increment(arg0)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(increment_token())
}
@ -1157,7 +1157,7 @@ mod generate_tests {
}
}
pub fn increment_token_callable() -> CallableFunction {
CallableFunction::from_plugin(increment_token())
increment_token().into()
}
pub fn increment_token_input_types() -> Box<[TypeId]> {
increment_token().input_types()
@ -1219,7 +1219,7 @@ mod generate_tests {
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("increment", FnAccess::Public,
&[core::any::TypeId::of::<FLOAT>()],
CallableFunction::from_plugin(increment_token()));
increment_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1230,12 +1230,12 @@ mod generate_tests {
) -> Result<Dynamic, Box<EvalAltResult>> {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0: &mut _ = &mut args[0usize].write_lock::<FLOAT>().unwrap();
let arg0 = &mut args[0usize].write_lock::<FLOAT>().unwrap();
Ok(Dynamic::from(increment(arg0)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(increment_token())
}
@ -1244,7 +1244,7 @@ mod generate_tests {
}
}
pub fn increment_token_callable() -> CallableFunction {
CallableFunction::from_plugin(increment_token())
increment_token().into()
}
pub fn increment_token_input_types() -> Box<[TypeId]> {
increment_token().input_types()
@ -1304,7 +1304,7 @@ mod generate_tests {
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(int_foo_token()));
int_foo_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1315,12 +1315,12 @@ mod generate_tests {
) -> Result<Dynamic, Box<EvalAltResult>> {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
Ok(Dynamic::from(int_foo(arg0)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(int_foo_token())
}
@ -1329,7 +1329,7 @@ mod generate_tests {
}
}
pub fn int_foo_token_callable() -> CallableFunction {
CallableFunction::from_plugin(int_foo_token())
int_foo_token().into()
}
pub fn int_foo_token_input_types() -> Box<[TypeId]> {
int_foo_token().input_types()
@ -1368,9 +1368,9 @@ mod generate_tests {
#[allow(unused_mut)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn("square", FnAccess::Public, &[core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(int_foo_token()));
int_foo_token().into());
m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(int_foo_token()));
int_foo_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1381,12 +1381,12 @@ mod generate_tests {
) -> Result<Dynamic, Box<EvalAltResult>> {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
Ok(Dynamic::from(int_foo(arg0)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(int_foo_token())
}
@ -1395,7 +1395,7 @@ mod generate_tests {
}
}
pub fn int_foo_token_callable() -> CallableFunction {
CallableFunction::from_plugin(int_foo_token())
int_foo_token().into()
}
pub fn int_foo_token_input_types() -> Box<[TypeId]> {
int_foo_token().input_types()
@ -1436,7 +1436,7 @@ mod generate_tests {
m.set_fn("set$squared", FnAccess::Public,
&[core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(int_foo_token()));
int_foo_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1448,12 +1448,12 @@ mod generate_tests {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
let arg1 = mem::take(args[1usize]).cast::<u64>();
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
Ok(Dynamic::from(int_foo(arg0, arg1)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(int_foo_token())
}
@ -1462,7 +1462,7 @@ mod generate_tests {
}
}
pub fn int_foo_token_callable() -> CallableFunction {
CallableFunction::from_plugin(int_foo_token())
int_foo_token().into()
}
pub fn int_foo_token_input_types() -> Box<[TypeId]> {
int_foo_token().input_types()
@ -1503,11 +1503,11 @@ mod generate_tests {
m.set_fn("set_sq", FnAccess::Public,
&[core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(int_foo_token()));
int_foo_token().into());
m.set_fn("set$squared", FnAccess::Public,
&[core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(int_foo_token()));
int_foo_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1519,12 +1519,12 @@ mod generate_tests {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
let arg1 = mem::take(args[1usize]).cast::<u64>();
let arg0: &mut _ = &mut args[0usize].write_lock::<u64>().unwrap();
let arg0 = &mut args[0usize].write_lock::<u64>().unwrap();
Ok(Dynamic::from(int_foo(arg0, arg1)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(int_foo_token())
}
@ -1533,7 +1533,7 @@ mod generate_tests {
}
}
pub fn int_foo_token_callable() -> CallableFunction {
CallableFunction::from_plugin(int_foo_token())
int_foo_token().into()
}
pub fn int_foo_token_input_types() -> Box<[TypeId]> {
int_foo_token().input_types()
@ -1574,7 +1574,7 @@ mod generate_tests {
m.set_fn("index$get$", FnAccess::Public,
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(get_by_index_token()));
get_by_index_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1586,12 +1586,12 @@ mod generate_tests {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
let arg1 = mem::take(args[1usize]).cast::<u64>();
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
Ok(Dynamic::from(get_by_index(arg0, arg1)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(get_by_index_token())
}
@ -1601,7 +1601,7 @@ mod generate_tests {
}
}
pub fn get_by_index_token_callable() -> CallableFunction {
CallableFunction::from_plugin(get_by_index_token())
get_by_index_token().into()
}
pub fn get_by_index_token_input_types() -> Box<[TypeId]> {
get_by_index_token().input_types()
@ -1642,11 +1642,11 @@ mod generate_tests {
m.set_fn("get", FnAccess::Public,
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(get_by_index_token()));
get_by_index_token().into());
m.set_fn("index$get$", FnAccess::Public,
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>()],
CallableFunction::from_plugin(get_by_index_token()));
get_by_index_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1658,12 +1658,12 @@ mod generate_tests {
debug_assert_eq!(args.len(), 2usize,
"wrong arg count: {} != {}", args.len(), 2usize);
let arg1 = mem::take(args[1usize]).cast::<u64>();
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
Ok(Dynamic::from(get_by_index(arg0, arg1)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(get_by_index_token())
}
@ -1673,7 +1673,7 @@ mod generate_tests {
}
}
pub fn get_by_index_token_callable() -> CallableFunction {
CallableFunction::from_plugin(get_by_index_token())
get_by_index_token().into()
}
pub fn get_by_index_token_input_types() -> Box<[TypeId]> {
get_by_index_token().input_types()
@ -1715,7 +1715,7 @@ mod generate_tests {
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
CallableFunction::from_plugin(set_by_index_token()));
set_by_index_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1728,12 +1728,12 @@ mod generate_tests {
"wrong arg count: {} != {}", args.len(), 3usize);
let arg1 = mem::take(args[1usize]).cast::<u64>();
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(set_by_index_token())
}
@ -1744,7 +1744,7 @@ mod generate_tests {
}
}
pub fn set_by_index_token_callable() -> CallableFunction {
CallableFunction::from_plugin(set_by_index_token())
set_by_index_token().into()
}
pub fn set_by_index_token_input_types() -> Box<[TypeId]> {
set_by_index_token().input_types()
@ -1786,12 +1786,12 @@ mod generate_tests {
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
CallableFunction::from_plugin(set_by_index_token()));
set_by_index_token().into());
m.set_fn("index$set$", FnAccess::Public,
&[core::any::TypeId::of::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
CallableFunction::from_plugin(set_by_index_token()));
set_by_index_token().into());
if flatten {} else {}
}
#[allow(non_camel_case_types)]
@ -1804,12 +1804,12 @@ mod generate_tests {
"wrong arg count: {} != {}", args.len(), 3usize);
let arg1 = mem::take(args[1usize]).cast::<u64>();
let arg2 = mem::take(args[2usize]).cast::<FLOAT>();
let arg0: &mut _ = &mut args[0usize].write_lock::<MyCollection>().unwrap();
let arg0 = &mut args[0usize].write_lock::<MyCollection>().unwrap();
Ok(Dynamic::from(set_by_index(arg0, arg1, arg2)))
}
fn is_method_call(&self) -> bool { true }
fn is_varadic(&self) -> bool { false }
fn is_variadic(&self) -> bool { false }
fn clone_boxed(&self) -> Box<dyn PluginFunction> {
Box::new(set_by_index_token())
}
@ -1820,7 +1820,7 @@ mod generate_tests {
}
}
pub fn set_by_index_token_callable() -> CallableFunction {
CallableFunction::from_plugin(set_by_index_token())
set_by_index_token().into()
}
pub fn set_by_index_token_input_types() -> Box<[TypeId]> {
set_by_index_token().input_types()

View File

@ -14,8 +14,8 @@ Easy
* Easily [call a script-defined function]({{rootUrl}}/engine/call-fn.md) from Rust.
* Very few additional dependencies (right now only [`smallvec`](https://crates.io/crates/smallvec/));
for [`no-std`] builds, a number of additional dependencies are pulled in to provide for functionalities that used to be in `std`.
* Very few additional dependencies - right now only [`smallvec`](https://crates.io/crates/smallvec/) plus crates for procedural macros;
for [`no-std`] and `WASM` builds, a number of additional dependencies are pulled in to provide for missing functionalities.
Fast
----

View File

@ -3,6 +3,8 @@ What is Rhai
{{#include ../links.md}}
![Rhai Logo]({{rootUrl}}/images/rhai_logo.png)
Rhai is an embedded scripting language and evaluation engine for Rust that gives a safe and easy way
to add scripting to any application.
@ -13,3 +15,27 @@ Versions
This Book is for version **{{version}}** of Rhai.
For the latest development version, see [here]({{rootUrl}}/vnext/).
Etymology of the name "Rhai"
---------------------------
### As per Rhai's author Johnathan Turner
In the beginning there was [ChaiScript](http://chaiscript.com),
which is an embedded scripting language for C++.
Originally it was intended to be a scripting language similar to **JavaScript**.
With java being a kind of hot beverage, the new language was named after
another hot beverage - **Chai**, which is the word for "tea" in many world languages
and, in particular, a popular kind of milk tea consumed in India.
Later, when the novel implementation technique behind ChaiScript was ported from C++ to Rust,
logically the `C` was changed to an `R` to make it "RhaiScript", or just "Rhai".
### On the origin of the temporary Rhai logo
One of Rhai's maintainers, Stephen Chung, was thinking about a logo when he accidentally
came across a copy of _Catcher in the Rye_ in a restaurant. The rest was history.
It is temporary until it becomes official, that is...

View File

@ -3,13 +3,11 @@ Licensing
{{#include ../links.md}}
Rhai is licensed under either:
Rhai is licensed under either of the following, at your choice:
* [Apache License, Version 2.0]({{repoHome}}/LICENSE-APACHE.txt), or
* [MIT license]({{repoHome}}/LICENSE-MIT.txt)
at your choice.
* [MIT license]({{repoHome}}/LICENSE-MIT.txt).
Unless explicitly stated otherwise, any contribution intentionally submitted for inclusion in this crate,
as defined in the Apache-2.0 license, shall be dual-licensed as above,

View File

@ -14,7 +14,7 @@ It doesn't attempt to be a new language. For example:
There is, however, a built-in [object map] type which is adequate for most uses.
It is possible to simulate [object-oriented programming (OOP)][OOP] by storing [function pointers]
in [object map] properties, turning them into _methods_.
or [closures] in [object map] properties, turning them into _methods_.
* No first-class functions - Code your functions in Rust instead, and register them with Rhai.
@ -22,22 +22,32 @@ It doesn't attempt to be a new language. For example:
* 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 first-class closures - do your closure magic in Rust instead: [turn a Rhai scripted function into a Rust closure]({{rootUrl}}/engine/call-fn.md).
But you can [curry][currying] a [function pointer] with arguments to simulate it somewhat.
There is, however, support for simulated [closures] via [currying] a [function pointer] with
captured shared variables.
* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races. The purpose of Rhai is not
to be extremely _fast_, but to make it as easy as possible to integrate with native Rust applications.
* No byte-codes/JIT - Rhai has an AST-walking interpreter which will not win any speed races.
The purpose of Rhai is not to be extremely _fast_, but to make it as easy as possible to
integrate with native Rust applications.
* No formal language grammar - Rhai uses a hand-coded lexer, a hand-coded top-down recursive-descent parser
for statements and a Pratt parser for expressions.
This lack of formalism allows the parser itself to be exposed as a service in order to support
[disabling keywords/operators][disable keywords and operators], adding [custom operators],
and defining [custom syntax].
Do Not Write The Next 4D VR Game in Rhai
---------------------------------------
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting advanced language features
such as classes, inheritance, first-class functions, closures, concurrency, byte-codes, JIT etc.
Due to this intended usage, Rhai deliberately keeps the language simple and small by omitting
advanced language features such as classes, inheritance, first-class functions, closures,
concurrency, byte-codes VM, JIT etc.
Avoid the temptation to write full-fledge application logic entirely in Rhai - that use case is best fulfilled by
more complete languages such as JavaScript or Lua.
Avoid the temptation to write full-fledge application logic entirely in Rhai -
that use case is best fulfilled by more complete languages such as JavaScript or Lua.
Thin Dynamic Wrapper Layer Over Rust Code
@ -47,7 +57,8 @@ In actual practice, it is usually best to expose a Rust API into Rhai for script
All the core functionalities should be written in Rust, with Rhai being the dynamic _control_ layer.
This is similar to some dynamic languages where most of the core functionalities reside in a C/C++ standard library.
This is similar to some dynamic languages where most of the core functionalities reside in a C/C++
standard library.
Another similar scenario is a web front-end driving back-end services written in a systems language.
In this case, JavaScript takes the role of Rhai while the back-end language, well... it can actually also be Rust.

View File

@ -5,7 +5,7 @@ Related Resources
Other online documentation resources for Rhai:
* [`crates.io`](https://crates.io/crates/rhai/) - Rhai crate
* [`crates.io`](https://crates.io/crates/rhai) - Rhai crate
* [`DOCS.RS`](https://docs.rs/rhai) - Rhai API documentation
@ -15,6 +15,6 @@ Other online documentation resources for Rhai:
Other cool projects to check out:
* [ChaiScript](http://chaiscript.com/) - A strong inspiration for Rhai. An embedded scripting language for C++ that I helped created many moons ago, now being led by my cousin.
* [ChaiScript](http://chaiscript.com) - A strong inspiration for Rhai. An embedded scripting language for C++.
* Check out the list of [scripting languages for Rust](https://github.com/rust-unofficial/awesome-rust#scripting) on [awesome-rust](https://github.com/rust-unofficial/awesome-rust)

View File

@ -10,3 +10,9 @@ The following targets and builds are support by Rhai:
* WebAssembly ([WASM])
* [`no-std`]
Minimum Rust Version
--------------------
The minimum version of Rust required to compile Rhai is `1.45.0`.

View File

@ -13,8 +13,6 @@ This section covers advanced features such as:
* [Script optimization].
* [Domain-Specific Languages][DSL].
* Low-level [function registration API]({{rootUrl}}/rust/register-raw.md)
* The dreaded (or beloved for those with twisted tastes) [`eval`] statement.
* [Domain-Specific Languages][DSL].

View File

@ -65,7 +65,7 @@ anything that implements `AsMut<Dynamic>` (such as a simple array or a `Vec<Dyna
```rust
let result = engine.call_fn_dynamic(
&mut scope, // scope to use
ast.into(), // get 'Module' from 'AST'
&ast, // AST containing the functions
"hello", // function entry-point
None, // 'this' pointer, if any
[ String::from("abc").into(), 123_i64.into() ] // arguments
@ -85,7 +85,7 @@ let mut value: Dynamic = 1_i64.into();
let result = engine.call_fn_dynamic(
&mut scope,
ast.into(),
&ast,
"action",
Some(&mut value), // binding the 'this' pointer
[ 41_i64.into() ]

View File

Before

Width:  |  Height:  |  Size: 10 KiB

After

Width:  |  Height:  |  Size: 10 KiB

View File

@ -49,8 +49,8 @@ f.call!(41); // <- syntax error: capturing is not allowed in method-c
No Mutations
------------
Variables in the calling scope are captured as copies.
Changes to them do not reflect back to the calling scope.
Variables in the calling scope are captured as cloned copies.
Changes to them do **not** reflect back to the calling scope.
Rhai functions remain _pure_ in the sense that they can never mutate their environment.

View File

@ -15,10 +15,10 @@ forming a new, combined, group of functions.
In general, there are two types of _namespaces_ where functions are looked up:
| Namespace | Source | Lookup method | How Many |
| --------- | ---------------------------------------------------------------------- | --------------------------------- | :----------------------: |
| Global | `Engine::register_XXX` API, [`AST`] being evaluated, [packages] loaded | simple function name | one |
| Module | [`Module`] | namespace-qualified function name | as many as [`import`]-ed |
| Namespace | Source | Lookup method | Sub-modules? | Variables? |
| --------- | ------------------------------------------------------------------------------------- | ------------------------------ | :----------: | :--------: |
| Global | 1) `Engine::register_XXX` API<br/>2) [`AST`] being evaluated<br/>3) [packages] loaded | simple function name | ignored | ignored |
| Module | [`Module`] | module-qualified function name | yes | yes |
Global Namespace

View File

@ -16,8 +16,9 @@ The following standard methods (mostly defined in the [`BasicFnPackage`][package
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 |
| `call` | _arguments_ | calls the function matching the function pointer's name with the _arguments_ |
Examples
@ -143,7 +144,9 @@ to a function call while binding the object in the method call to the `this` poi
To achieve this, pass the `FnPtr` value as the _first_ argument to `call`:
```rust
fn add(x) { this += x; } // define function which uses 'this'
fn add(x) { // define function which uses 'this'
this += x;
}
let func = Fn("add"); // function pointer to 'add'

View File

@ -12,8 +12,13 @@ Like C, `continue` can be used to skip to the next iteration, by-passing all fol
To loop through a number sequence (with or without steps), use the `range` function to
return a numeric iterator.
Iterate Through Strings
-----------------------
Iterating through a [string] yields characters.
```rust
// Iterate through string, yielding characters
let s = "hello, world!";
for ch in s {
@ -23,8 +28,15 @@ for ch in s {
if x == '@' { break; } // break out of for loop
}
```
// Iterate through array
Iterate Through Arrays
----------------------
Iterating through an [array] yields cloned _copies_ of each element.
```rust
let array = [1, 3, 5, 7, 9, 42];
for x in array {
@ -34,8 +46,17 @@ for x in array {
if x == 42 { break; } // break out of for loop
}
```
// The 'range' function allows iterating from first to last-1
Iterate Through Numeric Ranges
-----------------------------
The `range` function allows iterating through a range of numbers
(not including the last number).
```rust
// Iterate starting from 0 and stopping at 49.
for x in range(0, 50) {
if x > 10 { continue; } // skip to the next iteration
@ -44,7 +65,7 @@ for x in range(0, 50) {
if x == 42 { break; } // break out of for loop
}
// The 'range' function also takes a step
// The 'range' function also takes a step.
for x in range(0, 50, 3) { // step by 3
if x > 10 { continue; } // skip to the next iteration
@ -52,8 +73,18 @@ for x in range(0, 50, 3) { // step by 3
if x == 42 { break; } // break out of for loop
}
```
// Iterate through object map
Iterate Through Object Maps
--------------------------
Two functions, `keys` and `values`, return [arrays] containing cloned _copies_
of all property names and values of an [object map], respectively.
These [arrays] can be iterated.
```rust
let map = #{a:1, b:3, c:5, d:7, e:9};
// Property names are returned in unsorted, random order

View File

@ -42,19 +42,6 @@ add2(42) == 44;
```
No Access to External Scope
--------------------------
Functions are not _closures_. They do not capture the calling environment and can only access their own parameters.
They cannot access variables external to the function itself.
```rust
let x = 42;
fn foo() { x } // <- syntax error: variable 'x' doesn't exist
```
Global Definitions Only
----------------------
@ -77,24 +64,52 @@ fn do_addition(x) {
```
Use Before Definition
--------------------
No Access to External Scope
--------------------------
Functions are not _closures_. They do not capture the calling environment
and can only access their own parameters.
They cannot access variables external to the function itself.
```rust
let x = 42;
fn foo() { x } // <- syntax error: variable 'x' doesn't exist
```
But Can Call Other Functions
---------------------------
All functions in the same [`AST`] can call each other.
```rust
fn foo(x) { x + 1 } // function defined in the global namespace
fn bar(x) { foo(x) } // OK! function 'foo' can be called
```
Use Before Definition Allowed
----------------------------
Unlike C/C++, functions in Rhai can be defined _anywhere_ at 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, such as JavaScript's `function` keyword.
Arguments Passed by Value
------------------------
Arguments are Passed by Value
----------------------------
Functions defined in script always take [`Dynamic`] parameters (i.e. the parameter can be of any type).
Functions defined in script always take [`Dynamic`] parameters (i.e. they can be of any types).
Therefore, functions with the same name and same _number_ of parameters are equivalent.
It is important to remember that all arguments are passed by _value_, so all Rhai script-defined functions
are _pure_ (i.e. they never modify their arguments).
All arguments are passed by _value_, so all Rhai script-defined functions are _pure_
(i.e. they never modify their arguments).
Any update to an argument will **not** be reflected back to the caller.
```rust
@ -113,8 +128,8 @@ x == 500; // 'x' is NOT changed!
`this` - Simulating an Object Method
-----------------------------------
Functions can also be called in method-call style. When this is the case, the keyword '`this`'
binds to the object in the method call and can be changed.
Script-defined functions can also be called in method-call style.
When this happens, the keyword '`this`' binds to the object in the method call and can be changed.
```rust
fn change() { // not that the object does not need a parameter

View File

@ -39,8 +39,8 @@ array[0].update(); // <- call in method-call style will update 'a'
**IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.**
Number of Parameters
--------------------
Number of Parameters in Methods
------------------------------
Native Rust methods registered with an [`Engine`] take _one additional parameter_ more than
an equivalent method coded in script, where the object is accessed via the `this` pointer instead.
@ -53,15 +53,43 @@ The following table illustrates the differences:
| Rhai script | _N_ | `this` (of type `&mut T`) | `Fn(x: U, y: V)` |
`&mut` is Efficient, Except for `ImmutableString`
-----------------------------------------------
`&mut` is Efficient, Except for `&mut ImmutableString`
----------------------------------------------------
Using a `&mut` first parameter is highly encouraged when using types that are expensive to clone,
even when the intention is not to mutate that argument, because it avoids cloning that argument value.
For example, the `len` method of an [array] has the signature: `Fn(&mut Array) -> INT`.
The array itself is not modified in any way, but using a `&mut` parameter avoids a cloning that would
otherwise have happened if the signature were `Fn(Array) -> INT`.
Even when a function is never intended to be a method - for example an operator,
it is still sometimes beneficial to make it method-like (i.e. with a first `&mut` parameter)
if the first parameter is not modified.
For types that are expensive to clone (remember, all function calls are passed cloned
copies of argument values), this may result in a significant performance boost.
For primary types that are cheap to clone (e.g. those that implement `Copy`), including `ImmutableString`,
this is not necessary.
```rust
// This is a type that is very expensive to clone.
#[derive(Debug, Clone)]
struct VeryComplexType { ... }
// Calculate some value by adding 'VeryComplexType' with an integer number.
fn do_add(obj: &VeryComplexType, offset: i64) -> i64 {
...
}
engine
.register_type::<VeryComplexType>()
.register_fn("+", add_pure /* or add_method*/);
// Very expensive to call, as the 'VeryComplexType' is cloned before each call.
fn add_pure(obj: VeryComplexType, offset: i64) -> i64 {
do_add(obj, offset)
}
// Efficient to call, as only a reference to the 'VeryComplexType' is passed.
fn add_method(obj: &mut VeryComplexType, offset: i64) -> i64 {
do_add(obj, offset)
}
```

View File

@ -29,7 +29,15 @@ Modifying an `ImmutableString` causes it first to be cloned, and then the modifi
`ImmutableString` should be used in place of `String` for function parameters because using
`String` is very inefficient (the `String` argument is cloned during every call).
A alternative is to use `&str` which maps straight to `ImmutableString`.
A alternative is to use `&str` which de-sugars to `ImmutableString`.
```rust
fn slow(s: String) -> i64 { ... } // string is cloned each call
fn fast1(s: ImmutableString) -> i64 { ... } // cloning 'ImmutableString' is cheap
fn fast2(s: &str) -> i64 { ... } // de-sugars to above
```
String and Character Literals

View File

@ -16,8 +16,9 @@ if some_bad_condition_has_happened {
throw; // defaults to empty exception text: ""
```
Exceptions thrown via `throw` in the script can be captured by matching `Err(Box<EvalAltResult::ErrorRuntime(` _reason_ `,` _position_ `)>)`
with the exception text captured by the first parameter.
Exceptions thrown via `throw` in the script can be captured in Rust by matching
`Err(Box<EvalAltResult::ErrorRuntime(reason, position)>)` with the exception text
captured by `reason`.
```rust
let result = engine.eval::<i64>(r#"

View File

@ -46,15 +46,22 @@ struct Config {
### Make Shared Object
```rust
let config: Rc<RefCell<Config>> = Rc::new(RefCell::new(Default::default()));
pub type SharedConfig = Rc<RefCell<Config>>;
```
Note: Use `Arc<Mutex<T>>` or `Arc<RwLock<T>>` when using the [`sync`] feature because the function
must then be `Send + Sync`.
```rust
let config: SharedConfig = Rc::new(RefCell::new(Default::default()));
```
### Register Config API
The trick to building a Config API is to clone the shared configuration object and
move it into each function registration as a closure.
move it into each function registration via a closure.
It is not possible to use a [plugin module] to achieve this, so each function must
Therefore, it is not possible to use a [plugin module] to achieve this, and each function must
be registered one after another.
```rust

View File

@ -35,7 +35,7 @@ but only through exposing an abstract API primarily made up of functions.
Use this when the API is relatively simple and clean, and the number of functions is small enough.
For a complex API involving lots of functions, or an API that is object-based,
For a complex API involving lots of functions, or an API that has a clear object structure,
use the [Singleton Command Object]({{rootUrl}}/patterns/singleton.md) pattern instead.
@ -59,15 +59,22 @@ impl EnergizerBunny {
### Wrap API in Shared Object
```rust
let bunny: Rc<RefCell<EnergizerBunny>> = Rc::new(RefCell::(EnergizerBunny::new()));
pub type SharedBunny = Rc<RefCell<EnergizerBunny>>;
```
Note: Use `Arc<Mutex<T>>` or `Arc<RwLock<T>>` when using the [`sync`] feature because the function
must then be `Send + Sync`.
```rust
let bunny: SharedBunny = Rc::new(RefCell::(EnergizerBunny::new()));
```
### Register Control API
The trick to building a Control API is to clone the shared API object and
move it into each function registration as a closure.
move it into each function registration via a closure.
It is not possible to use a [plugin module] to achieve this, so each function must
Therefore, it is not possible to use a [plugin module] to achieve this, and each function must
be registered one after another.
```rust

View File

@ -41,7 +41,7 @@ wrapping the system in a shared, interior-mutated object.
This is the other way which involves directly exposing the data structures of the external system
as a name singleton object in the scripting space.
Use this when the API is complex and clearly object-based.
Use this when the API is complex but has a clear object structure.
For a relatively simple API that is action-based and not object-based,
use the [Control Layer]({{rootUrl}}/patterns/control.md) pattern instead.
@ -68,13 +68,16 @@ impl EnergizerBunny {
### Wrap Command Object Type as Shared
```rust
let SharedBunnyType = Rc<RefCell<EnergizerBunny>>;
pub type SharedBunny = Rc<RefCell<EnergizerBunny>>;
```
Note: Use `Arc<Mutex<T>>` or `Arc<RwLock<T>>` when using the [`sync`] feature because the function
must then be `Send + Sync`.
### Register the Custom Type
```rust
engine.register_type_with_name::<SharedBunnyType>("EnergizerBunny");
engine.register_type_with_name::<SharedBunny>("EnergizerBunny");
```
### Develop a Plugin with Methods and Getters/Setters
@ -82,18 +85,18 @@ engine.register_type_with_name::<SharedBunnyType>("EnergizerBunny");
The easiest way to develop a complete set of API for a [custom type] is via a [plugin module].
```rust
use rhai::plugins::*;
use rhai::plugin::*;
#[export_module]
pub mod bunny_api {
pub const MAX_SPEED: i64 = 100;
#[rhai_fn(get = "power")]
pub fn get_power(bunny: &mut SharedBunnyType) -> bool {
pub fn get_power(bunny: &mut SharedBunny) -> bool {
bunny.borrow().is_going()
}
#[rhai_fn(set = "power")]
pub fn set_power(bunny: &mut SharedBunnyType, on: bool) {
pub fn set_power(bunny: &mut SharedBunny, on: bool) {
if on {
if bunny.borrow().is_going() {
println!("Still going...");
@ -109,7 +112,7 @@ pub mod bunny_api {
}
}
#[rhai_fn(get = "speed")]
pub fn get_speed(bunny: &mut SharedBunnyType) -> i64 {
pub fn get_speed(bunny: &mut SharedBunny) -> i64 {
if bunny.borrow().is_going() {
bunny.borrow().get_speed()
} else {
@ -117,7 +120,7 @@ pub mod bunny_api {
}
}
#[rhai_fn(set = "speed", return_raw)]
pub fn set_speed(bunny: &mut SharedBunnyType, speed: i64)
pub fn set_speed(bunny: &mut SharedBunny, speed: i64)
-> Result<Dynamic, Box<EvalAltResult>>
{
if speed <= 0 {
@ -131,12 +134,12 @@ pub mod bunny_api {
Ok(().into())
}
}
pub fn turn_left(bunny: &mut SharedBunnyType) {
pub fn turn_left(bunny: &mut SharedBunny) {
if bunny.borrow().is_going() {
bunny.borrow_mut().turn(true);
}
}
pub fn turn_right(bunny: &mut SharedBunnyType) {
pub fn turn_right(bunny: &mut SharedBunny) {
if bunny.borrow().is_going() {
bunny.borrow_mut().turn(false);
}
@ -149,7 +152,7 @@ engine.load_package(exported_module!(bunny_api));
### Push Constant Command Object into Custom Scope
```rust
let bunny: SharedBunnyType = Rc::new(RefCell::(EnergizerBunny::new()));
let bunny: SharedBunny = Rc::new(RefCell::(EnergizerBunny::new()));
let mut scope = Scope::new();

View File

@ -29,7 +29,7 @@ To register the plugin function, simply call `register_exported_fn!`. The name
any text string, so it is possible to register _overloaded_ functions as well as operators.
```rust
use rhai::plugins::*; // import macros
use rhai::plugin::*; // import macros
#[export_fn]
fn increment(num: &mut i64) {
@ -55,7 +55,7 @@ A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does n
have the appropriate return type.
```rust
use rhai::plugins::*; // import macros
use rhai::plugin::*; // a "prelude" import for macros
#[export_fn]
#[rhai_fn(return_raw)]

View File

@ -9,5 +9,5 @@ functionality.
Instead of using the large `Engine::register_XXX` API or the parallel `Module::set_fn_XXX` API,
a _plugin_ simplifies the work of creating and registering new functionality in an [`Engine`].
Plugins are processed via a set of procedural macros under the `rhai::plugins` module. These
Plugins are processed via a set of procedural macros under the `rhai::plugin` module. These
allow registering Rust functions directly in the Engine, or adding Rust modules as packages.

View File

@ -4,6 +4,20 @@ Export a Rust Module to Rhai
{{#include ../links.md}}
Prelude
-------
When using the plugins system, the entire `rhai::plugin` module must be imported as a prelude
because code generated will these imports.
```rust
use rhai::plugin::*;
```
`#[export_module]` and `exported_module!`
----------------------------------------
When applied to a Rust module, the `#[export_module]` attribute generates the necessary
code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions, constants
and sub-modules.
@ -14,18 +28,11 @@ and is custom fit to each exported item.
This Rust module can then either be loaded into an [`Engine`] as a normal [module] or
registered as a [custom package]. This is done by using the `exported_module!` macro.
`#[export_module]` and `exported_module!`
----------------------------------------
Apply `#[export_module]` onto a Rust module to register automatically construct a Rhai [module],
which can then be loaded into an [`Engine`].
All `pub` functions become registered functions, all `pub` constants become [module] constant variables,
and all sub-modules become Rhai sub-modules.
```rust
use rhai::plugins::*; // a "prelude" import for macros
use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
mod my_module {
@ -117,7 +124,7 @@ Operators (which require function names that are not valid for Rust) can also be
Registering the same function name with the same parameter types will cause a parsing error.
```rust
use rhai::plugins::*; // a "prelude" import for macros
use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
mod my_module {
@ -147,7 +154,7 @@ Functions can be marked as [getters/setters] and [indexers] for [custom types] v
attribute, which is applied on a function level.
```rust
use rhai::plugins::*; // a "prelude" import for macros
use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
mod my_module {
@ -188,7 +195,7 @@ This is especially useful for the `name = "..."`, `get = "..."` and `set = "..."
to give multiple alternative names to the same function.
```rust
use rhai::plugins::*; // a "prelude" import for macros
use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
mod my_module {
@ -221,7 +228,7 @@ A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does n
have the appropriate return type.
```rust
use rhai::plugins::*; // a "prelude" import for macros
use rhai::plugin::*; // a "prelude" import for macros
#[export_module]
mod my_module {

View File

@ -3,8 +3,16 @@ Disable Custom Types
{{#include ../links.md}}
`no_object` Feature
-------------------
The custom types API `register_type`, `register_type_with_name`, `register_get`, `register_get_result`,
`register_set`, `register_set_result` and `register_get_set` are not available under [`no_object`].
`no_index` Feature
------------------
The indexers API `register_indexer_get`, `register_indexer_get_result`, `register_indexer_set`,
`register_indexer_set_result`, and `register_indexer_get_set` are also not available under [`no_index`].

View File

@ -11,7 +11,7 @@ Getters and setters are disabled when the [`no_object`] feature is used.
| `Engine` API | Description | Return Value of Function |
| --------------------- | ------------------------------------------------- | :-----------------------------------: |
| `register_get` | register a getter | _any_ |
| `register_get` | register a getter | _any_ `T: Clone` |
| `register_set` | register a setter | _none_ |
| `register_get_set` | short-hand to register both a getter and a setter | _none_ |
| `register_get_result` | register a getter | `Result<Dynamic, Box<EvalAltResult>>` |

View File

@ -15,7 +15,7 @@ Indexers are disabled when the [`no_index`] feature is used.
| `Engine` API | Description | Return Value of Function |
| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: |
| `register_indexer_get` | register an index getter | _any_ |
| `register_indexer_get` | register an index getter | _any_ `T: Clone` |
| `register_indexer_set` | register an index setter | _none_ |
| `register_indexer_get_set` | short-hand to register both an index getter and a setter | _none_ |
| `register_indexer_get_result` | register an index getter | `Result<Dynamic, Box<EvalAltResult>>` |

View File

@ -6,16 +6,23 @@ Operator Overloading
In Rhai, a lot of functionalities are actually implemented as functions, including basic operations
such as arithmetic calculations.
For example, in the expression "`a + b`", the `+` operator is _not_ built in, but calls a function named "`+`" instead!
For example, in the expression "`a + b`", the `+` operator calls a function named "`+`"!
```rust
let x = a + b;
let x = +(a, b); // <- the above is equivalent to this function call
```
Similarly, comparison operators including `==`, `!=` etc. are all implemented as functions,
with the stark exception of `&&` and `||`. Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators),
`&&` and `||` are handled specially and _not_ via a function; as a result, overriding them has no effect at all.
with the stark exception of `&&` and `||`.
`&&` and `||` Cannot Be Overloaded
---------------------------------
Because they [_short-circuit_]({{rootUrl}}/language/logic.md#boolean-operators), `&&` and `||` are
handled specially and _not_ via a function; as a result, overriding them has no effect at all.
Overload Operator via Rust Function

View File

@ -60,7 +60,7 @@ def_package!(rhai:MyPackage:"My own personal super package", module, {
Create a Custom Package from a Plugin Module
-------------------------------------------
By far the easiest way to create a custom module is to call `rhai::plugins::combine_with_exported_module!`
By far the easiest way to create a custom module is to call `rhai::plugin::combine_with_exported_module!`
from within `rhai::def_package!` which simply merges in all the functions defined within a [plugin module].
In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage`, are implemented.
@ -113,8 +113,8 @@ def_package!(rhai:MyPackage:"My own personal super package", module, {
//
// The sub-module 'my_sub_module' is flattened and its functions registered at the top level.
//
// The text string name in the middle parameter can be anything and is reserved for future use;
// it is recommended to be an ID string that uniquely identifies the module.
// The text string name in the second parameter can be anything and is reserved for future use;
// it is recommended to be an ID string that uniquely identifies the plugin module.
//
// The constant variable, 'MY_NUMBER', is ignored.
//

View File

@ -3,7 +3,8 @@ Load a Plugin Module as a Package
{{#include ../../links.md}}
[Plugin modules] can be loaded as a package just like a normal [module].
[Plugin modules] can be loaded as a package just like a normal [module]
via the `exported_module!` macro.
```rust
use rhai::Engine;

View File

@ -35,13 +35,12 @@ engine.register_raw_fn(
// Therefore, get a '&mut' reference to the first argument _last_.
// Alternatively, use `args.split_first_mut()` etc. to split the slice first.
let y: i64 = *args[1].read_lock::<i64>() // get a reference to the second argument
.unwrap(); // then copying it because it is a primary type
let y = *args[1].read_lock::<i64>().unwrap(); // get a reference to the second argument
// then copy it because it is a primary type
let y: i64 = std::mem::take(args[1]).cast::<i64>(); // alternatively, directly 'consume' it
let y = std::mem::take(args[1]).cast::<i64>(); // alternatively, directly 'consume' it
let x: &mut i64 = args[0].write_lock::<i64>() // get a '&mut' reference to the
.unwrap(); // first argument
let x = args[0].write_lock::<i64>().unwrap(); // get a '&mut' reference to the first argument
*x += y; // perform the action
@ -51,7 +50,7 @@ engine.register_raw_fn(
// The above is the same as (in fact, internally they are equivalent):
engine.register_fn("increment_by", |x: &mut i64, y: i64| x += y);
engine.register_fn("increment_by", |x: &mut i64, y: i64| *x += y);
```
@ -153,7 +152,8 @@ is a _shared value_ created by [capturing][automatic currying] variables from [c
Shared values are implemented as `Rc<RefCell<Dynamic>>` (`Arc<RwLock<Dynamic>>` under [`sync`]).
If the value is _not_ a shared value, or if running under [`no_closure`] where there is
no [capturing][automatic currying], this API de-sugars to a simple `downcast_ref` and `downcast_mut`.
no [capturing][automatic currying], this API de-sugars to a simple `Dynamic::downcast_ref` and
`Dynamic::downcast_mut`.
If the value is a shared value, then it is first locked and the returned lock guard
then allows access to the underlying value in the specified type.
@ -171,9 +171,9 @@ to partition the slice:
let (first, rest) = args.split_first_mut().unwrap();
// Mutable reference to the first parameter
let this_ptr: &mut A = &mut *first.write_lock::<A>().unwrap();
let this_ptr = &mut *first.write_lock::<A>().unwrap();
// Immutable reference to the second value parameter
// This can be mutable but there is no point because the parameter is passed by value
let value_ref: &B = &*rest[0].read_lock::<B>().unwrap();
let value_ref = &*rest[0].read_lock::<B>().unwrap();
```

View File

@ -6,8 +6,9 @@ Traits
A number of traits, under the `rhai::` module namespace, provide additional functionalities.
| Trait | Description | Methods |
| ------------------ | ---------------------------------------------------------------------------------------- | --------------------------------------- |
| ------------------------ | ------------------------------------------------------------------ | --------------------------------------------------------------------- |
| `RegisterFn` | trait for registering functions | `register_fn` |
| `RegisterResultFn` | trait for registering fallible functions returning `Result<Dynamic, Box<EvalAltResult>>` | `register_result_fn` |
| `RegisterResultFn` | trait for registering [fallible functions] | `register_result_fn` |
| `Func` | trait for creating Rust closures from script | `create_from_ast`, `create_from_script` |
| `ModuleResolver` | trait implemented by module resolution services | `resolve` |
| `ModuleResolver` | trait implemented by [module resolution][module resolver] services | `resolve` |
| `plugin::PluginFunction` | trait implemented by [plugin] functions | `call`, `is_method_call`, `is_variadic`, `clone_boxed`, `input_types` |

View File

@ -1504,7 +1504,7 @@ impl Engine {
let mut arg_values = args.into_vec();
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
let result = self.call_fn_dynamic_raw(scope, ast, name, &mut None, args.as_mut())?;
let result = self.call_fn_dynamic_raw(scope, ast.lib(), name, &mut None, args.as_mut())?;
let typ = self.map_type_name(result.type_name());
@ -1578,7 +1578,7 @@ impl Engine {
) -> FuncReturn<Dynamic> {
let mut args: StaticVec<_> = arg_values.as_mut().iter_mut().collect();
self.call_fn_dynamic_raw(scope, lib, name, &mut this_ptr, args.as_mut())
self.call_fn_dynamic_raw(scope, lib.as_ref(), name, &mut this_ptr, args.as_mut())
}
/// Call a script function defined in an `AST` with multiple `Dynamic` arguments.
@ -1593,12 +1593,11 @@ impl Engine {
pub(crate) fn call_fn_dynamic_raw(
&self,
scope: &mut Scope,
lib: impl AsRef<Module>,
lib: &Module,
name: &str,
this_ptr: &mut Option<&mut Dynamic>,
args: &mut [&mut Dynamic],
) -> FuncReturn<Dynamic> {
let lib = lib.as_ref();
let fn_def = get_script_function_by_signature(lib, name, args.len(), true)
.ok_or_else(|| EvalAltResult::ErrorFunctionNotFound(name.into(), Position::none()))?;

View File

@ -199,10 +199,10 @@ pub type FnAny =
/// A standard function that gets an iterator from a type.
pub type IteratorFn = fn(Dynamic) -> Box<dyn Iterator<Item = Dynamic>>;
#[cfg(feature = "sync")]
pub type SharedPluginFunction = Arc<dyn PluginFunction + Send + Sync>;
#[cfg(not(feature = "sync"))]
pub type SharedPluginFunction = Rc<dyn PluginFunction>;
pub type FnPlugin = dyn PluginFunction;
#[cfg(feature = "sync")]
pub type FnPlugin = dyn PluginFunction + Send + Sync;
/// A standard callback function.
#[cfg(not(feature = "sync"))]
@ -221,8 +221,8 @@ pub enum CallableFunction {
Method(Shared<FnAny>),
/// An iterator function.
Iterator(IteratorFn),
/// A plugin-defined function,
Plugin(SharedPluginFunction),
/// A plugin function,
Plugin(Shared<FnPlugin>),
/// A script-defined function.
#[cfg(not(feature = "no_function"))]
Script(Shared<ScriptFnDef>),
@ -300,7 +300,7 @@ impl CallableFunction {
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) | Self::Plugin(_) => false,
}
}
/// Is this a plugin-defined function?
/// Is this a plugin function?
pub fn is_plugin_fn(&self) -> bool {
match self {
Self::Plugin(_) => true,
@ -389,9 +389,9 @@ impl CallableFunction {
/// # Panics
///
/// Panics if the `CallableFunction` is not `Plugin`.
pub fn get_plugin_fn<'s>(&'s self) -> SharedPluginFunction {
pub fn get_plugin_fn<'s>(&'s self) -> &FnPlugin {
match self {
Self::Plugin(f) => f.clone(),
Self::Plugin(f) => f.as_ref(),
Self::Pure(_) | Self::Method(_) | Self::Iterator(_) => unreachable!(),
#[cfg(not(feature = "no_function"))]
@ -406,17 +406,9 @@ impl CallableFunction {
pub fn from_method(func: Box<FnAny>) -> Self {
Self::Method(func.into())
}
#[cfg(feature = "sync")]
/// Create a new `CallableFunction::Plugin`.
pub fn from_plugin(plugin: impl PluginFunction + 'static + Send + Sync) -> Self {
Self::Plugin(Arc::new(plugin))
}
#[cfg(not(feature = "sync"))]
/// Create a new `CallableFunction::Plugin`.
pub fn from_plugin(plugin: impl PluginFunction + 'static) -> Self {
Self::Plugin(Rc::new(plugin))
pub fn from_plugin(func: impl PluginFunction + 'static + SendSync) -> Self {
Self::Plugin((Box::new(func) as Box<FnPlugin>).into())
}
}
@ -445,3 +437,15 @@ impl From<Shared<ScriptFnDef>> for CallableFunction {
Self::Script(_func)
}
}
impl<T: PluginFunction + 'static + SendSync> From<T> for CallableFunction {
fn from(func: T) -> Self {
Self::from_plugin(func)
}
}
impl From<Shared<FnPlugin>> for CallableFunction {
fn from(func: Shared<FnPlugin>) -> Self {
Self::Plugin(func.into())
}
}

View File

@ -7,118 +7,12 @@ use crate::engine::Engine;
use crate::fn_native::{CallableFunction, FnAny, FnCallArgs, SendSync};
use crate::module::Module;
use crate::parser::FnAccess;
use crate::plugin::Plugin;
use crate::r#unsafe::unsafe_cast_box;
use crate::result::EvalAltResult;
use crate::utils::ImmutableString;
use crate::stdlib::{any::TypeId, boxed::Box, mem, string::String};
/// A trait to register custom plugins with the `Engine`.
///
/// A plugin consists of a number of functions. All functions will be registered with the engine.
pub trait RegisterPlugin<PL: crate::plugin::Plugin> {
/// Allow extensions of the engine's behavior.
///
/// This can include importing modules, registering functions to the global name space, and
/// more.
///
/// # Example
///
/// ```
/// # #[cfg(not(feature = "no_float"))]
/// use rhai::FLOAT as NUMBER;
/// # #[cfg(feature = "no_float")]
/// use rhai::INT as NUMBER;
/// # #[cfg(not(feature = "no_module"))]
/// use rhai::{Module, ModuleResolver, RegisterFn, RegisterPlugin};
/// # #[cfg(not(feature = "no_module"))]
/// use rhai::plugin::*;
/// # #[cfg(not(feature = "no_module"))]
/// use rhai::module_resolvers::*;
///
/// // A function we want to expose to Rhai.
/// #[derive(Copy, Clone)]
/// struct DistanceFunction();
///
/// # #[cfg(not(feature = "no_module"))]
/// impl PluginFunction for DistanceFunction {
/// fn is_method_call(&self) -> bool { false }
/// fn is_varadic(&self) -> bool { false }
///
/// fn call(&self, args: &mut[&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>> {
/// let x1: NUMBER = std::mem::take(args[0]).clone().cast::<NUMBER>();
/// let y1: NUMBER = std::mem::take(args[1]).clone().cast::<NUMBER>();
/// let x2: NUMBER = std::mem::take(args[2]).clone().cast::<NUMBER>();
/// let y2: NUMBER = std::mem::take(args[3]).clone().cast::<NUMBER>();
/// # #[cfg(not(feature = "no_float"))]
/// let square_sum = (y2 - y1).abs().powf(2.0) + (x2 -x1).abs().powf(2.0);
/// # #[cfg(feature = "no_float")]
/// let square_sum = (y2 - y1).abs().pow(2) + (x2 -x1).abs().pow(2);
/// Ok(Dynamic::from(square_sum))
/// }
///
/// fn clone_boxed(&self) -> Box<dyn PluginFunction> {
/// Box::new(DistanceFunction())
/// }
///
/// fn input_types(&self) -> Box<[std::any::TypeId]> {
/// vec![std::any::TypeId::of::<NUMBER>(),
/// std::any::TypeId::of::<NUMBER>(),
/// std::any::TypeId::of::<NUMBER>(),
/// std::any::TypeId::of::<NUMBER>()].into_boxed_slice()
/// }
/// }
///
/// // A simple custom plugin. This should not usually be done with hand-written code.
/// #[derive(Copy, Clone)]
/// pub struct AdvancedMathPlugin();
///
/// # #[cfg(not(feature = "no_module"))]
/// impl Plugin for AdvancedMathPlugin {
/// fn register_contents(self, engine: &mut Engine) {
/// // Plugins are allowed to have side-effects on the engine.
/// engine.register_fn("get_mystic_number", || { 42 as NUMBER });
///
/// // Main purpose: create a module to expose the functions to Rhai.
/// //
/// // This is currently a hack. There needs to be a better API here for "plugin"
/// // modules.
/// let mut m = Module::new();
/// m.set_fn("euclidean_distance".to_string(), FnAccess::Public,
/// &[std::any::TypeId::of::<NUMBER>(),
/// std::any::TypeId::of::<NUMBER>(),
/// std::any::TypeId::of::<NUMBER>(),
/// std::any::TypeId::of::<NUMBER>()],
/// CallableFunction::from_plugin(DistanceFunction()));
/// let mut r = StaticModuleResolver::new();
/// r.insert("Math::Advanced".to_string(), m);
/// engine.set_module_resolver(Some(r));
/// }
/// }
///
///
/// # fn main() -> Result<(), Box<rhai::EvalAltResult>> {
///
/// # #[cfg(not(feature = "no_module"))] {
/// let mut engine = Engine::new();
/// engine.register_plugin(AdvancedMathPlugin());
///
/// # #[cfg(feature = "no_float")]
/// assert_eq!(engine.eval::<NUMBER>(
/// r#"import "Math::Advanced" as math;
/// let x = math::euclidean_distance(0, 1, 0, get_mystic_number()); x"#)?, 1681);
/// # #[cfg(not(feature = "no_float"))]
/// assert_eq!(engine.eval::<NUMBER>(
/// r#"import "Math::Advanced" as math;
/// let x = math::euclidean_distance(0.0, 1.0, 0.0, get_mystic_number()); x"#)?, 1681.0);
/// # } // end cfg
/// # Ok(())
/// # }
/// ```
fn register_plugin(&mut self, plugin: PL);
}
/// Trait to register custom functions with the `Engine`.
pub trait RegisterFn<FN, ARGS, RET> {
/// Register a custom function with the `Engine`.
@ -223,12 +117,6 @@ pub fn by_value<T: Variant + Clone>(data: &mut Dynamic) -> T {
}
}
impl<PL: Plugin> RegisterPlugin<PL> for Engine {
fn register_plugin(&mut self, plugin: PL) {
plugin.register_contents(self);
}
}
/// This macro creates a closure wrapping a registered function.
macro_rules! make_func {
($fn:ident : $map:expr ; $($par:ident => $let:stmt => $convert:expr => $arg:expr),*) => {

View File

@ -86,7 +86,7 @@ pub use any::Dynamic;
pub use engine::Engine;
pub use error::{ParseError, ParseErrorType};
pub use fn_native::{FnPtr, IteratorFn};
pub use fn_register::{RegisterFn, RegisterPlugin, RegisterResultFn};
pub use fn_register::{RegisterFn, RegisterResultFn};
pub use module::Module;
pub use parser::{ImmutableString, AST, INT};
pub use result::EvalAltResult;

View File

@ -154,6 +154,25 @@ impl Module {
}
}
/// Is the module empty?
///
/// # Examples
///
/// ```
/// use rhai::Module;
///
/// let module = Module::new();
/// assert!(module.is_empty());
/// ```
pub fn is_empty(&self) -> bool {
self.functions.is_empty()
&& self.all_functions.is_empty()
&& self.variables.is_empty()
&& self.all_variables.is_empty()
&& self.modules.is_empty()
&& self.type_iterators.is_empty()
}
/// Clone the module, optionally skipping the index.
fn do_clone(&self, clone_index: bool) -> Self {
Self {
@ -1078,10 +1097,18 @@ impl Module {
/// Combine another module into this module.
/// The other module is consumed to merge into this module.
pub fn combine(&mut self, other: Self) -> &mut Self {
if !other.modules.is_empty() {
self.modules.extend(other.modules.into_iter());
}
if !other.variables.is_empty() {
self.variables.extend(other.variables.into_iter());
}
if !other.functions.is_empty() {
self.functions.extend(other.functions.into_iter());
}
if !other.type_iterators.is_empty() {
self.type_iterators.extend(other.type_iterators.into_iter());
}
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
@ -1092,13 +1119,20 @@ impl Module {
/// The other module is consumed to merge into this module.
/// Sub-modules are flattened onto the root module, with higher level overriding lower level.
pub fn combine_flatten(&mut self, other: Self) -> &mut Self {
if !other.modules.is_empty() {
other.modules.into_iter().for_each(|(_, m)| {
self.combine_flatten(m);
});
}
if !other.variables.is_empty() {
self.variables.extend(other.variables.into_iter());
}
if !other.functions.is_empty() {
self.functions.extend(other.functions.into_iter());
}
if !other.type_iterators.is_empty() {
self.type_iterators.extend(other.type_iterators.into_iter());
}
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
@ -1117,19 +1151,23 @@ impl Module {
mut _filter: &mut impl FnMut(FnAccess, &str, usize) -> bool,
) -> &mut Self {
#[cfg(not(feature = "no_function"))]
if !other.modules.is_empty() {
for (k, v) in &other.modules {
let mut m = Self::new();
m.merge_filtered(v, _filter);
self.modules.insert(k.clone(), m);
}
}
#[cfg(feature = "no_function")]
if !other.modules.is_empty() {
self.modules
.extend(other.modules.iter().map(|(k, v)| (k.clone(), v.clone())));
}
if !other.variables.is_empty() {
self.variables
.extend(other.variables.iter().map(|(k, v)| (k.clone(), v.clone())));
}
if !other.functions.is_empty() {
self.functions.extend(
other
.functions
@ -1143,10 +1181,12 @@ impl Module {
})
.map(|(&k, v)| (k, v.clone())),
);
}
if !other.type_iterators.is_empty() {
self.type_iterators
.extend(other.type_iterators.iter().map(|(&k, v)| (k, v.clone())));
}
self.all_functions.clear();
self.all_variables.clear();
self.indexed = false;
@ -1280,13 +1320,21 @@ impl Module {
name,
num_args,
move |engine: &Engine, lib: &Module, args: &mut [&mut Dynamic]| {
let mut lib_merged = lib.clone();
let mut lib_merged;
let unified_lib = if lib.is_empty() {
// In the special case of the main script not defining any function
&ast_lib
} else {
lib_merged = lib.clone();
lib_merged.merge(&ast_lib);
&lib_merged
};
engine
.call_fn_dynamic_raw(
&mut Scope::new(),
&lib_merged,
&unified_lib,
&fn_name,
&mut None,
args,

View File

@ -1,42 +1,38 @@
//! Module defining plugins in Rhai for use by plugin authors.
//! Module defining macros for developing _plugins_.
pub use crate::{
fn_native::CallableFunction, stdlib::any::TypeId, stdlib::boxed::Box, stdlib::format,
stdlib::mem, stdlib::string::ToString, stdlib::vec as new_vec, stdlib::vec::Vec, Dynamic,
Engine, EvalAltResult, FnAccess, ImmutableString, Module, RegisterResultFn,
};
pub use crate::any::Dynamic;
pub use crate::engine::Engine;
pub use crate::fn_native::CallableFunction;
pub use crate::fn_register::{RegisterFn, RegisterResultFn};
pub use crate::module::Module;
pub use crate::parser::FnAccess;
pub use crate::result::EvalAltResult;
pub use crate::utils::ImmutableString;
pub use crate::stdlib::{any::TypeId, boxed::Box, format, mem, string::ToString, vec as new_vec};
#[cfg(not(features = "no_module"))]
pub use rhai_codegen::*;
#[cfg(features = "no_module")]
pub use rhai_codegen::{export_fn, register_exported_fn};
#[cfg(features = "sync")]
/// Represents an externally-written plugin for the Rhai interpreter.
/// Trait implemented by a _plugin function_.
/// This trait should not be used directly.
///
/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead.
pub trait Plugin: Send {
fn register_contents(self, engine: &mut Engine);
}
#[cfg(not(features = "sync"))]
/// Represents an externally-written plugin for the Rhai interpreter.
///
/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead.
pub trait Plugin: Send + Sync {
fn register_contents(self, engine: &mut Engine);
}
/// Represents a function that is statically defined within a plugin.
///
/// This trait should not be used directly. Use the `#[plugin]` procedural attribute instead.
/// Use the `#[export_module]` and `#[export_fn]` procedural attributes instead.
pub trait PluginFunction {
fn is_method_call(&self) -> bool;
fn is_varadic(&self) -> bool;
/// Call the plugin function with the arguments provided.
fn call(&self, args: &mut [&mut Dynamic]) -> Result<Dynamic, Box<EvalAltResult>>;
/// Is this plugin function a method?
fn is_method_call(&self) -> bool;
/// Is this plugin function variadic?
fn is_variadic(&self) -> bool;
/// Convert a plugin function into a boxed trait object.
fn clone_boxed(&self) -> Box<dyn PluginFunction>;
/// Return a boxed slice of type ID's of the function's parameters.
fn input_types(&self) -> Box<[TypeId]>;
}

View File

@ -17,11 +17,6 @@ use crate::stdlib::{
string::{String, ToString},
};
#[cfg(not(feature = "sync"))]
use crate::stdlib::rc::Rc;
#[cfg(feature = "sync")]
use crate::stdlib::sync::Arc;
/// A general expression evaluation trait object.
#[cfg(not(feature = "sync"))]
pub type FnCustomSyntaxEval = dyn Fn(
@ -175,10 +170,7 @@ impl Engine {
let syntax = CustomSyntax {
segments,
#[cfg(not(feature = "sync"))]
func: Rc::new(func),
#[cfg(feature = "sync")]
func: Arc::new(func),
func: (Box::new(func) as Box<FnCustomSyntaxEval>).into(),
scope_delta,
};

View File

@ -61,7 +61,8 @@ impl BuildHasher for StraightHasherBuilder {
}
}
/// Calculate a `u64` hash key from a module-qualified function name and parameter types.
/// [INTERNALS] Calculate a `u64` hash key from a module-qualified function name and parameter types.
/// Exported under the `internals` feature only.
///
/// Module names are passed in via `&str` references from an iterator.
/// Parameter types are passed in via `TypeId` values from an iterator.

View File

@ -237,6 +237,9 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
fn add_len(x, y) {
x + len(y)
}
fn cross_call(x) {
calc(x)
}
private fn hidden() {
throw "you shouldn't see me!";
}
@ -285,6 +288,10 @@ fn test_module_from_ast() -> Result<(), Box<EvalAltResult>> {
engine.eval::<INT>(r#"import "testing" as ttt; ttt::calc(999)"#)?,
1000
);
assert_eq!(
engine.eval::<INT>(r#"import "testing" as ttt; ttt::cross_call(999)"#)?,
1000
);
assert_eq!(
engine.eval::<INT>(r#"import "testing" as ttt; ttt::add_len(ttt::foo, ttt::hello)"#)?,
59