diff --git a/codegen/Cargo.toml b/codegen/Cargo.toml index 377b8f91..8cd109f3 100644 --- a/codegen/Cargo.toml +++ b/codegen/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "rhai_codegen" -version = "0.2.0" +version = "0.3.0" edition = "2018" authors = ["jhwgh1968"] description = "Procedural macro support package for Rhai, a scripting language for Rust" diff --git a/codegen/src/function.rs b/codegen/src/function.rs index c0d50588..64df0cd3 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -20,6 +20,12 @@ use syn::{ use crate::attrs::{ExportInfo, ExportScope, ExportedParams}; +#[derive(Clone, Debug, Eq, PartialEq, Copy, Hash)] +pub enum FnNamespaceAccess { + Global, + Internal, +} + #[derive(Clone, Debug, Eq, PartialEq)] pub enum Index { Get, @@ -82,8 +88,9 @@ pub(crate) struct ExportedFnParams { pub name: Option>, pub return_raw: bool, pub skip: bool, - pub span: Option, pub special: FnSpecialAccess, + pub namespace: Option, + pub span: Option, } pub const FN_GET: &str = "get$"; @@ -119,6 +126,7 @@ impl ExportedParams for ExportedFnParams { let mut name = Vec::new(); let mut return_raw = false; let mut skip = false; + let mut namespace = None; let mut special = FnSpecialAccess::None; for attr in attrs { let crate::attrs::AttrItem { @@ -194,12 +202,30 @@ impl ExportedParams for ExportedFnParams { } } } - ("return_raw", None) => return_raw = true, ("index_get", Some(s)) | ("index_set", Some(s)) | ("return_raw", Some(s)) => { return Err(syn::Error::new(s.span(), "extraneous value")) } + ("return_raw", None) => return_raw = true, + ("return_raw", Some(s)) => { + return Err(syn::Error::new(s.span(), "extraneous value")) + } ("skip", None) => skip = true, ("skip", Some(s)) => return Err(syn::Error::new(s.span(), "extraneous value")), + ("global", Some(s)) | ("internal", Some(s)) => { + return Err(syn::Error::new(s.span(), "extraneous value")) + } + ("global", None) => { + if namespace.is_some() { + return Err(syn::Error::new(key.span(), "conflicting namespace")); + } + namespace = Some(FnNamespaceAccess::Global); + } + ("internal", None) => { + if namespace.is_some() { + return Err(syn::Error::new(key.span(), "conflicting namespace")); + } + namespace = Some(FnNamespaceAccess::Internal); + } (attr, _) => { return Err(syn::Error::new( key.span(), @@ -214,6 +240,7 @@ impl ExportedParams for ExportedFnParams { return_raw, skip, special, + namespace, span: Some(span), ..Default::default() }) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index f577c1df..e12189ae 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -339,7 +339,48 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream }; let gen_mod_path = crate::register::generated_module_path(&rust_modpath); let tokens = quote! { - #module_expr.set_fn(#export_name, FnAccess::Public, + #module_expr.set_fn(#export_name, FnNamespace::Internal, FnAccess::Public, + #gen_mod_path::token_input_types().as_ref(), + #gen_mod_path::token_callable()); + }; + proc_macro::TokenStream::from(tokens) +} + +/// Macro to register a _plugin function_ into a Rhai `Module` and expose it globally. +/// +/// # Usage +/// +/// ``` +/// # use rhai::{Engine, EvalAltResult}; +/// use rhai::plugin::*; +/// +/// #[export_fn] +/// fn my_plugin_function(x: i64) -> i64 { +/// x * 2 +/// } +/// +/// # fn main() -> Result<(), Box> { +/// let mut engine = Engine::new(); +/// +/// let mut module = Module::new(); +/// set_exported_global_fn!(module, "func", my_plugin_function); +/// +/// engine.load_module("test", module); +/// +/// assert_eq!(engine.eval::("func(21)")?, 42); +/// # Ok(()) +/// # } +/// ``` +#[proc_macro] +pub fn set_exported_global_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream { + let (module_expr, export_name, rust_modpath) = match crate::register::parse_register_macro(args) + { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let gen_mod_path = crate::register::generated_module_path(&rust_modpath); + let tokens = quote! { + #module_expr.set_fn(#export_name, FnNamespace::Global, FnAccess::Public, #gen_mod_path::token_input_types().as_ref(), #gen_mod_path::token_callable()); }; diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index b7d160bc..3981e3b9 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -4,7 +4,7 @@ use quote::{quote, ToTokens}; use crate::attrs::ExportScope; use crate::function::flatten_type_groups; -use crate::function::{ExportedFn, FnSpecialAccess}; +use crate::function::{ExportedFn, FnNamespaceAccess, FnSpecialAccess}; use crate::module::Module; pub(crate) type ExportedConst = (String, Box, syn::Expr); @@ -80,6 +80,7 @@ pub(crate) fn generate_body( function.name().span(), ); let reg_names = function.exported_names(); + let mut namespace = FnNamespaceAccess::Internal; let fn_input_types: Vec = function .arg_list() @@ -123,12 +124,22 @@ pub(crate) fn generate_body( }) .collect(); + if let Some(ns) = function.params().namespace { + namespace = ns; + } + for fn_literal in reg_names { set_fn_stmts.push( - syn::parse2::(quote! { - m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*], - #fn_token_name().into()); - }) + match namespace { + FnNamespaceAccess::Global => syn::parse2::(quote! { + m.set_fn(#fn_literal, FnNamespace::Global, FnAccess::Public, &[#(#fn_input_types),*], + #fn_token_name().into()); + }), + FnNamespaceAccess::Internal => syn::parse2::(quote! { + m.set_fn(#fn_literal, FnNamespace::Internal, FnAccess::Public, &[#(#fn_input_types),*], + #fn_token_name().into()); + }), + } .unwrap(), ); } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 554d1901..56d3290c 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -295,7 +295,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, &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -330,6 +330,68 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_single_arg_global_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod one_global_fn { + #[rhai_fn(global)] + pub fn add_one_to(x: INT) -> INT { + x + 1 + } + } + }; + + let expected_tokens = quote! { + pub mod one_global_fn { + pub fn add_one_to(x: INT) -> INT { + x + 1 + } + #[allow(unused_imports)] + use super::*; + + pub fn rhai_module_generate() -> Module { + let mut m = Module::new(); + rhai_generate_into_module(&mut m, false); + m + } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_fn("add_one_to", FnNamespace::Global, FnAccess::Public, &[core::any::TypeId::of::()], + add_one_to_token().into()); + if flatten {} else {} + } + #[allow(non_camel_case_types)] + struct add_one_to_token(); + impl PluginFunction for add_one_to_token { + fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> Result> { + debug_assert_eq!(args.len(), 1usize, + "wrong arg count: {} != {}", args.len(), 1usize); + let arg0 = mem::take(args[0usize]).cast::(); + Ok(Dynamic::from(add_one_to(arg0))) + } + + fn is_method_call(&self) -> bool { false } + fn is_variadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(add_one_to_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::()].into_boxed_slice() + } + } + pub fn add_one_to_token_callable() -> CallableFunction { + add_one_to_token().into() + } + pub fn add_one_to_token_input_types() -> Box<[TypeId]> { + add_one_to_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_single_arg_fn_module() { let input_tokens: TokenStream = quote! { @@ -355,7 +417,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::()], + m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], add_one_to_token().into()); if flatten {} else {} } @@ -427,10 +489,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::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], add_one_to_token().into()); - m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_n_to_token().into()); if flatten {} else {} } @@ -519,8 +581,8 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} } @@ -584,14 +646,14 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("add", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("+", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("+", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); - m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), - core::any::TypeId::of::()], + m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), + core::any::TypeId::of::()], add_together_token().into()); if flatten {} else {} } @@ -831,7 +893,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, &[], + m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public, &[], get_mystic_number_token().into()); if flatten {} else {} } @@ -921,7 +983,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("print_out_to", FnAccess::Public, + m.set_fn("print_out_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} @@ -983,7 +1045,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("print_out_to", FnAccess::Public, + m.set_fn("print_out_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], print_out_to_token().into()); if flatten {} else {} @@ -1045,7 +1107,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("increment", FnAccess::Public, + m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} @@ -1110,7 +1172,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("increment", FnAccess::Public, + m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} @@ -1195,7 +1257,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("increment", FnAccess::Public, + m.set_fn("increment", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], increment_token().into()); if flatten {} else {} @@ -1279,7 +1341,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::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1341,9 +1403,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::()], + m.set_fn("square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], + m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::()], int_foo_token().into()); if flatten {} else {} } @@ -1405,7 +1467,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set$squared", FnAccess::Public, + m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); @@ -1470,11 +1532,11 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set_sq", FnAccess::Public, + m.set_fn("set_sq", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); - m.set_fn("set$squared", FnAccess::Public, + m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], int_foo_token().into()); @@ -1539,7 +1601,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("index$get$", FnAccess::Public, + m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1605,11 +1667,11 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("get", FnAccess::Public, + m.set_fn("get", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); - m.set_fn("index$get$", FnAccess::Public, + m.set_fn("index$get$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], get_by_index_token().into()); @@ -1675,7 +1737,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("index$set$", FnAccess::Public, + m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1744,12 +1806,12 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_fn("set", FnAccess::Public, + m.set_fn("set", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], set_by_index_token().into()); - m.set_fn("index$set$", FnAccess::Public, + m.set_fn("index$set$", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], diff --git a/codegen/ui_tests/rhai_fn_global_multiple.rs b/codegen/ui_tests/rhai_fn_global_multiple.rs new file mode 100644 index 00000000..45e2e325 --- /dev/null +++ b/codegen/ui_tests/rhai_fn_global_multiple.rs @@ -0,0 +1,28 @@ +use rhai::plugin::*; + +#[derive(Clone)] +pub struct Point { + x: f32, + y: f32, +} + +#[export_module] +pub mod test_module { + pub use super::Point; + #[rhai_fn(global, global)] + pub fn test_fn(input: Point) -> bool { + input.x > input.y + } +} + +fn main() { + let n = Point { + x: 0.0, + y: 10.0 + }; + if test_module::test_fn(n) { + println!("yes"); + } else { + println!("no"); + } +} diff --git a/codegen/ui_tests/rhai_fn_global_multiple.stderr b/codegen/ui_tests/rhai_fn_global_multiple.stderr new file mode 100644 index 00000000..328c641b --- /dev/null +++ b/codegen/ui_tests/rhai_fn_global_multiple.stderr @@ -0,0 +1,11 @@ +error: conflicting namespace + --> $DIR/rhai_fn_global_multiple.rs:12:23 + | +12 | #[rhai_fn(global, global)] + | ^^^^^^ + +error[E0433]: failed to resolve: use of undeclared type or module `test_module` + --> $DIR/rhai_fn_global_multiple.rs:23:8 + | +23 | if test_module::test_fn(n) { + | ^^^^^^^^^^^ use of undeclared type or module `test_module` diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md index f539be19..6c47575f 100644 --- a/doc/src/plugins/function.md +++ b/doc/src/plugins/function.md @@ -11,11 +11,12 @@ individual functions instead of a full-blown [plugin module]. Macros ------ -| Macro | Signature | Description | -| ----------------------- | ------------------------------------------------------------------ | --------------------------------------------------------------- | -| `#[export_fn]` | apply to rust function defined in a Rust module | exports the function | -| `register_exported_fn!` | `register_exported_fn!(&mut `_engine_`, "`_name_`", `_function_`)` | registers the function into an [`Engine`] under a specific name | -| `set_exported_fn!` | `set_exported_fn!(&mut `_module_`, "`_name_`", `_function_`)` | registers the function into a [`Module`] under a specific name | +| Macro | Signature | Description | +| ------------------------- | -------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------- | +| `#[export_fn]` | apply to rust function defined in a Rust module | exports the function | +| `register_exported_fn!` | `register_exported_fn!(&mut `_engine_`, "`_name_`", `_function_`)` | registers the function into an [`Engine`] under a specific name | +| `set_exported_fn!` | `set_exported_fn!(&mut `_module_`, "`_name_`", `_function_`)` | registers the function into a [`Module`] under a specific name | +| `set_exported_global_fn!` | `set_exported_global_fn!(&mut `_module_`, "`_name_`", `_function_`)` | registers the function into a [`Module`] under a specific name, exposing it to the global namespace | `#[export_fn]` and `register_exported_fn!` diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 1bd676c5..34d198cc 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -481,12 +481,14 @@ Inner attributes can be applied to the inner items of a module to tweak the expo Parameters should be set on inner attributes to specify the desired behavior. -| Attribute Parameter | Use with | Apply to | Description | -| ------------------- | --------------------------- | ----------------------------------------------------- | ------------------------------------------------------ | -| `skip` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | do not export this function/sub-module | -| `name = "..."` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | registers function/sub-module under the specified name | -| `get = "..."` | `#[rhai_fn]` | `pub fn (&mut Type) -> Value` | registers a getter for the named property | -| `set = "..."` | `#[rhai_fn]` | `pub fn (&mut Type, Value)` | registers a setter for the named property | -| `index_get` | `#[rhai_fn]` | `pub fn (&mut Type, INT) -> Value` | registers an index getter | -| `index_set` | `#[rhai_fn]` | `pub fn (&mut Type, INT, Value)` | registers an index setter | -| `return_raw` | `#[rhai_fn]` | `pub fn (...) -> Result>` | marks this as a [fallible function] | +| Attribute Parameter | Use with | Apply to | Description | +| ------------------- | --------------------------- | ----------------------------------------------------- | ------------------------------------------------------- | +| `skip` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | do not export this function/sub-module | +| `global` | `#[rhai_fn]` | function | expose this function to the global namespace | +| `internal` | `#[rhai_fn]` | function | keep this function within the internal module namespace | +| `name = "..."` | `#[rhai_fn]`, `#[rhai_mod]` | function or sub-module | registers function/sub-module under the specified name | +| `get = "..."` | `#[rhai_fn]` | `pub fn (&mut Type) -> Value` | registers a getter for the named property | +| `set = "..."` | `#[rhai_fn]` | `pub fn (&mut Type, Value)` | registers a setter for the named property | +| `index_get` | `#[rhai_fn]` | `pub fn (&mut Type, INT) -> Value` | registers an index getter | +| `index_set` | `#[rhai_fn]` | `pub fn (&mut Type, INT, Value)` | registers an index setter | +| `return_raw` | `#[rhai_fn]` | `pub fn (...) -> Result>` | marks this as a [fallible function] |