Add global/internal parameters to rhai_fn.

This commit is contained in:
Stephen Chung 2020-11-17 12:09:56 +08:00
parent 999a87f86e
commit a19865d811
9 changed files with 237 additions and 54 deletions

View File

@ -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"

View File

@ -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<Vec<String>>,
pub return_raw: bool,
pub skip: bool,
pub span: Option<proc_macro2::Span>,
pub special: FnSpecialAccess,
pub namespace: Option<FnNamespaceAccess>,
pub span: Option<proc_macro2::Span>,
}
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()
})

View File

@ -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<EvalAltResult>> {
/// 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::<i64>("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());
};

View File

@ -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::Type>, 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<syn::Expr> = 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::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnAccess::Public, &[#(#fn_input_types),*],
#fn_token_name().into());
})
match namespace {
FnNamespaceAccess::Global => syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnNamespace::Global, FnAccess::Public, &[#(#fn_input_types),*],
#fn_token_name().into());
}),
FnNamespaceAccess::Internal => syn::parse2::<syn::Stmt>(quote! {
m.set_fn(#fn_literal, FnNamespace::Internal, FnAccess::Public, &[#(#fn_input_types),*],
#fn_token_name().into());
}),
}
.unwrap(),
);
}

View File

@ -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::<INT>()],
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<Dynamic, Box<EvalAltResult>> {
debug_assert_eq!(args.len(), 1usize,
"wrong arg count: {} != {}", args.len(), 1usize);
let arg0 = mem::take(args[0usize]).cast::<INT>();
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<dyn PluginFunction> {
Box::new(add_one_to_token())
}
fn input_types(&self) -> Box<[TypeId]> {
new_vec![TypeId::of::<INT>()].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::<Module>(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::<INT>()],
m.set_fn("add_one_to", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>()],
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::<INT>()],
m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>()],
add_one_to_token().into());
m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add_n", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
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::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
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::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
add_together_token().into());
m.set_fn("+", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("+", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
add_together_token().into());
m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
m.set_fn("add_together", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<INT>(),
core::any::TypeId::of::<INT>()],
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::<ImmutableString>()],
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::<ImmutableString>()],
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::<FLOAT>()],
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::<FLOAT>()],
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::<FLOAT>()],
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::<u64>()],
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<u64>()],
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::<u64>()],
m.set_fn("square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<u64>()],
int_foo_token().into());
m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::<u64>()],
m.set_fn("get$square", FnNamespace::Internal, FnAccess::Public, &[core::any::TypeId::of::<u64>()],
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::<u64>(),
core::any::TypeId::of::<u64>()],
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::<u64>(),
core::any::TypeId::of::<u64>()],
int_foo_token().into());
m.set_fn("set$squared", FnAccess::Public,
m.set_fn("set$squared", FnNamespace::Internal, FnAccess::Public,
&[core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<u64>()],
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::<MyCollection>(),
core::any::TypeId::of::<u64>()],
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::<MyCollection>(),
core::any::TypeId::of::<u64>()],
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::<MyCollection>(),
core::any::TypeId::of::<u64>()],
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::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
@ -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::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],
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::<MyCollection>(),
core::any::TypeId::of::<u64>(),
core::any::TypeId::of::<FLOAT>()],

View File

@ -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");
}
}

View File

@ -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`

View File

@ -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!`

View File

@ -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<Dynamic, Box<EvalAltResult>>` | 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<Dynamic, Box<EvalAltResult>>` | marks this as a [fallible function] |