From aed70db303e7db9dea5298404691334287757adc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 15 Sep 2020 12:30:40 +0800 Subject: [PATCH 01/34] Revert "Delete benchmark.yml" This reverts commit d547562c14df13f32ae4876f5b3b30cec4276ac3. --- .github/workflows/benchmark.yml | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 .github/workflows/benchmark.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml new file mode 100644 index 00000000..df310705 --- /dev/null +++ b/.github/workflows/benchmark.yml @@ -0,0 +1,29 @@ +name: Benchmark +on: + push: + branches: + - master + +jobs: + benchmark: + name: Run Rust benchmark + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v2 + - run: rustup toolchain update nightly && rustup default nightly + - name: Run benchmark + run: cargo +nightly bench | tee output.txt + - name: Store benchmark result + uses: rhysd/github-action-benchmark@v1 + with: + name: Rust Benchmark + tool: 'cargo' + output-file-path: output.txt + # Use personal access token instead of GITHUB_TOKEN due to https://github.community/t5/GitHub-Actions/Github-action-not-triggering-gh-pages-upon-push/td-p/26869/highlight/false + github-token: ${{ secrets.RHAI }} + auto-push: true + # Show alert with commit comment on detecting possible performance regression + alert-threshold: '200%' + comment-on-alert: true + fail-on-alert: true + alert-comment-cc-users: '@schungx' From 848bdf3f01b2129b62675341471a08eff4eea010 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:12:11 +0800 Subject: [PATCH 02/34] Add combine_with_exported_module. --- codegen/src/lib.rs | 14 ++++++++++++++ codegen/src/module.rs | 1 + codegen/src/rhai_module.rs | 14 ++++++++++++++ doc/src/plugins/module.md | 3 +++ doc/src/rust/packages/create.md | 11 +++++++---- src/packages/arithmetic.rs | 6 +++--- src/packages/array_basic.rs | 2 +- src/packages/eval.rs | 2 +- src/packages/fn_basic.rs | 2 +- src/packages/logic.rs | 2 +- src/packages/map_basic.rs | 2 +- src/packages/math_basic.rs | 4 ++-- src/packages/string_more.rs | 2 +- src/packages/time_basic.rs | 2 +- tests/plugins.rs | 14 +++++++++++++- xxx.rs | 0 16 files changed, 64 insertions(+), 17 deletions(-) create mode 100644 xxx.rs diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index b9d8880d..3763b47e 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -152,6 +152,20 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke proc_macro::TokenStream::from(tokens) } +#[proc_macro] +pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream { + #[allow(unused_variables)] + let (module_expr, export_name, module_path) = match crate::register::parse_register_macro(args) + { + Ok(triple) => triple, + Err(e) => return e.to_compile_error().into(), + }; + let tokens = quote! { + #module_path::rhai_generate_into_module(#module_expr); + }; + proc_macro::TokenStream::from(tokens) +} + #[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) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 1ebc25b8..d89bfbb9 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -191,6 +191,7 @@ impl Parse for Module { } } +#[allow(dead_code)] impl Module { pub fn attrs(&self) -> Option<&Vec> { self.mod_all.as_ref().map(|m| &m.attrs) diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index d318f351..911c7edc 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -17,6 +17,7 @@ pub(crate) fn generate_body( let mut set_fn_stmts: Vec = Vec::new(); let mut set_const_stmts: Vec = Vec::new(); let mut add_mod_blocks: Vec = Vec::new(); + let mut set_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); for (const_name, const_expr) in consts { @@ -54,6 +55,14 @@ pub(crate) fn generate_body( }) .unwrap(), ); + set_mod_blocks.push( + syn::parse2::(quote! { + #(#cfg_attrs)* { + self::#module_name::rhai_generate_into_module(m); + } + }) + .unwrap(), + ); } // NB: these are token streams, because reparsing messes up "> >" vs ">>" @@ -137,6 +146,11 @@ pub(crate) fn generate_body( #(#add_mod_blocks)* m } + pub fn rhai_generate_into_module(m: &mut Module) { + #(#set_fn_stmts)* + #(#set_const_stmts)* + #(#set_mod_blocks)* + } } }) .unwrap(); diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 40232140..24107fff 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -24,6 +24,9 @@ use rhai::plugins::*; // a "prelude" import for macros #[export_module] mod my_module { + // This constant will be registered as a the constant variable 'SOME_NUMBER'. + pub const SOME_NUMBER: i64 = 42; + // This function will be registered as 'greet'. pub fn greet(name: &str) -> String { format!("hello, {}!", name) diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index d212d38f..4f55cc13 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -63,8 +63,8 @@ By far the easiest way to create a custom module is to call `Module::merge_flatt In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage`, are implemented. -`rhai::plugins::exported_module!` generates a module from the [plugins][plugin module] definition, -and `Module::merge_flatten` consumes its, adding all its registered functions into the package itself. +`rhai::plugins::combine_with_exported_module!` adds all functions and constants from the +[plugins][plugin module] definition into the package itself. ```rust // Import necessary types and traits. @@ -94,7 +94,10 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { BasicArrayPackage::init(module); BasicMapPackage::init(module); - // Merge the plugin module into the custom package. - module.merge_flatten(exported_module!(my_module)); + // Merge all registered functions and constants from the plugin module into the custom package. + // + // 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. + combine_with_exported_module!(module, "my-functions", my_module)); }); ``` diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index d72e2e8b..f963e778 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -185,7 +185,7 @@ macro_rules! gen_signed_functions { macro_rules! reg_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+ ) => { $( - $mod_name.combine_flatten(exported_module!($root::$arg_type::functions)); + combine_with_exported_module!($mod_name, "arithmetic", $root::$arg_type::functions); )* } } @@ -208,8 +208,8 @@ def_package!(crate:ArithmeticPackage:"Basic arithmetic", lib, { // Basic arithmetic for floating-point #[cfg(not(feature = "no_float"))] { - lib.combine_flatten(exported_module!(f32_functions)); - lib.combine_flatten(exported_module!(f64_functions)); + combine_with_exported_module!(lib, "f32", f32_functions); + combine_with_exported_module!(lib, "f64", f64_functions); } }); diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index 767c4c5f..05599eac 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -58,7 +58,7 @@ macro_rules! reg_functions { } def_package!(crate:BasicArrayPackage:"Basic array utilities.", lib, { - lib.combine_flatten(exported_module!(array_functions)); + combine_with_exported_module!(lib, "array", array_functions); reg_functions!(lib += basic; INT, bool, char, ImmutableString, FnPtr, Array, Unit); diff --git a/src/packages/eval.rs b/src/packages/eval.rs index acebf216..bb5716d8 100644 --- a/src/packages/eval.rs +++ b/src/packages/eval.rs @@ -5,7 +5,7 @@ use crate::plugin::*; use crate::result::EvalAltResult; def_package!(crate:EvalPackage:"Disable 'eval'.", lib, { - lib.combine_flatten(exported_module!(eval_override)); + combine_with_exported_module!(lib, "eval", eval_override); }); #[export_module] diff --git a/src/packages/fn_basic.rs b/src/packages/fn_basic.rs index 61d18371..5ffb6cb1 100644 --- a/src/packages/fn_basic.rs +++ b/src/packages/fn_basic.rs @@ -3,7 +3,7 @@ use crate::fn_native::FnPtr; use crate::plugin::*; def_package!(crate:BasicFnPackage:"Basic Fn functions.", lib, { - lib.combine_flatten(exported_module!(fn_ptr_functions)); + combine_with_exported_module!(lib, "FnPtr", fn_ptr_functions); }); #[export_module] diff --git a/src/packages/logic.rs b/src/packages/logic.rs index 48e46047..554f30e3 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -45,7 +45,7 @@ macro_rules! gen_cmp_functions { macro_rules! reg_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $( - $mod_name.combine_flatten(exported_module!($root::$arg_type::functions)); + combine_with_exported_module!($mod_name, "logic", $root::$arg_type::functions); )* } } diff --git a/src/packages/map_basic.rs b/src/packages/map_basic.rs index a5983268..fde57db7 100644 --- a/src/packages/map_basic.rs +++ b/src/packages/map_basic.rs @@ -9,7 +9,7 @@ use crate::plugin::*; use crate::stdlib::vec::Vec; def_package!(crate:BasicMapPackage:"Basic object map utilities.", lib, { - lib.combine_flatten(exported_module!(map_functions)); + combine_with_exported_module!(lib, "map", map_functions); }); #[export_module] diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index ddbb640c..2fd2a9c4 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -48,10 +48,10 @@ def_package!(crate:BasicMathPackage:"Basic mathematic functions.", lib, { #[cfg(not(feature = "no_float"))] { // Floating point functions - lib.combine_flatten(exported_module!(float_functions)); + combine_with_exported_module!(lib, "float", float_functions); // Trig functions - lib.combine_flatten(exported_module!(trig_functions)); + combine_with_exported_module!(lib, "trig", trig_functions); reg_functions!(lib += basic_to_float::to_float(INT)); diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 5dfd4897..55fa4be0 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -57,7 +57,7 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str #[cfg(not(feature = "no_float"))] reg_functions!(lib += float; f32, f64); - lib.combine_flatten(exported_module!(string_functions)); + combine_with_exported_module!(lib, "string", string_functions); lib.set_raw_fn( "pad", diff --git a/src/packages/time_basic.rs b/src/packages/time_basic.rs index 80ba2637..caa5fc47 100644 --- a/src/packages/time_basic.rs +++ b/src/packages/time_basic.rs @@ -21,7 +21,7 @@ use instant::Instant; def_package!(crate:BasicTimePackage:"Basic timing utilities.", lib, { // Register date/time functions - lib.combine_flatten(exported_module!(time_functions)); + combine_with_exported_module!(lib, "time", time_functions); }); #[export_module] diff --git a/tests/plugins.rs b/tests/plugins.rs index cac8718f..928f0381 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -1,5 +1,6 @@ #![cfg(not(any(feature = "no_index", feature = "no_module")))] +use rhai::module_resolvers::StaticModuleResolver; use rhai::plugin::*; use rhai::{Engine, EvalAltResult, INT}; @@ -10,6 +11,8 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; + pub const MYSTIC_NUMBER: INT = 42 as INT; + #[cfg(not(feature = "no_object"))] pub mod feature { use rhai::{Array, Dynamic, EvalAltResult}; @@ -66,7 +69,7 @@ fn test_plugins_package() -> Result<(), Box> { let mut engine = Engine::new(); let mut m = Module::new(); - m.combine_flatten(exported_module!(test::special_array_package)); + combine_with_exported_module!(&mut m, "test", test::special_array_package); engine.load_package(m); reg_functions!(engine += greet::single(INT, bool, char)); @@ -83,5 +86,14 @@ fn test_plugins_package() -> Result<(), Box> { "6 kitties" ); + let mut resolver = StaticModuleResolver::new(); + resolver.insert("test", exported_module!(test::special_array_package)); + + engine.set_module_resolver(Some(resolver)); + assert_eq!( + engine.eval::(r#"import "test" as test; test::MYSTIC_NUMBER"#)?, + 42 + ); + Ok(()) } diff --git a/xxx.rs b/xxx.rs new file mode 100644 index 00000000..e69de29b From 6dc5a81d539e6cccb1194d3b843f57aaf15acfd7 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:36:24 +0800 Subject: [PATCH 03/34] Add cast to type for constants to avoid mis-typing. --- codegen/src/module.rs | 3 ++- codegen/src/rhai_module.rs | 6 +++--- tests/plugins.rs | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index d89bfbb9..85b1278c 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -140,12 +140,13 @@ impl Parse for Module { ref expr, ident, attrs, + ty, .. }) => { // #[cfg] attributes are not allowed on const declarations crate::attrs::deny_cfg_attr(&attrs)?; if let syn::Visibility::Public(_) = vis { - consts.push((ident.to_string(), expr.as_ref().clone())); + consts.push((ident.to_string(), ty.clone(), expr.as_ref().clone())); } } _ => {} diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 911c7edc..165f7438 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -6,7 +6,7 @@ use crate::attrs::ExportScope; use crate::function::ExportedFn; use crate::module::Module; -pub(crate) type ExportedConst = (String, syn::Expr); +pub(crate) type ExportedConst = (String, Box, syn::Expr); pub(crate) fn generate_body( fns: &mut [ExportedFn], @@ -20,11 +20,11 @@ pub(crate) fn generate_body( let mut set_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); - for (const_name, const_expr) in consts { + for (const_name, const_type, const_expr) in consts { let const_literal = syn::LitStr::new(&const_name, proc_macro2::Span::call_site()); set_const_stmts.push( syn::parse2::(quote! { - m.set_var(#const_literal, #const_expr); + m.set_var(#const_literal, (#const_expr) as #const_type); }) .unwrap(), ); diff --git a/tests/plugins.rs b/tests/plugins.rs index 928f0381..2bee84e4 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -11,7 +11,7 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; - pub const MYSTIC_NUMBER: INT = 42 as INT; + pub const MYSTIC_NUMBER: INT = 42; #[cfg(not(feature = "no_object"))] pub mod feature { From f1d85ae99c287966e8e22c1a4911793a33ff9762 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:12:11 +0800 Subject: [PATCH 04/34] Add combine_with_exported_module. --- tests/plugins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins.rs b/tests/plugins.rs index 2bee84e4..928f0381 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -11,7 +11,7 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; - pub const MYSTIC_NUMBER: INT = 42; + pub const MYSTIC_NUMBER: INT = 42 as INT; #[cfg(not(feature = "no_object"))] pub mod feature { From 6126f0cb101e29646d20348fd5a10a6395970a7b Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:36:24 +0800 Subject: [PATCH 05/34] Add cast to type for constants to avoid mis-typing. --- tests/plugins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins.rs b/tests/plugins.rs index 928f0381..2bee84e4 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -11,7 +11,7 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; - pub const MYSTIC_NUMBER: INT = 42 as INT; + pub const MYSTIC_NUMBER: INT = 42; #[cfg(not(feature = "no_object"))] pub mod feature { From d57ce9c0508db7ca31ff6b6d48a28d26a7c72e73 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:36:40 +0800 Subject: [PATCH 06/34] Remove warning about unused constants in modules. --- codegen/src/module.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 85b1278c..4b829693 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -282,6 +282,7 @@ impl Module { // Regenerate the module with the new content added. Ok(quote! { #(#mod_attrs)* + #[allow(dead_code)] pub mod #mod_name { #(#orig_content)* #(#inner_modules)* From 654da2db8a2a051d57bae4c5d7e144af5821dafb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 23:13:07 +0800 Subject: [PATCH 07/34] Fix tests. --- codegen/src/test/module.rs | 4 ++-- doc/src/rust/packages/create.md | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 2 deletions(-) diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 2bf48752..677cd040 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -109,7 +109,7 @@ mod module_tests { assert_eq!(item_mod.submodules().len(), 1); assert_eq!(&item_mod.submodules()[0].consts()[0].0, "MYSTIC_NUMBER"); assert_eq!( - item_mod.submodules()[0].consts()[0].1, + item_mod.submodules()[0].consts()[0].2, syn::parse2::(quote! { 42 }).unwrap() ); } @@ -170,7 +170,7 @@ mod module_tests { assert_eq!(item_mod.consts().len(), 1); assert_eq!(&item_mod.consts()[0].0, "MYSTIC_NUMBER"); assert_eq!( - item_mod.consts()[0].1, + item_mod.consts()[0].2, syn::parse2::(quote! { 42 }).unwrap() ); } diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 4f55cc13..53ac17a0 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -66,6 +66,9 @@ In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage `rhai::plugins::combine_with_exported_module!` adds all functions and constants from the [plugins][plugin module] definition into the package itself. +All sub-modules are _flattened_ (i.e. all functions and constants defined within sub-modules are registered +at the top level) and so there will not be any sub-modules added to the package. + ```rust // Import necessary types and traits. use rhai::{ @@ -84,6 +87,14 @@ mod my_module { pub fn get_num() -> i64 { 42 } + + // This is a sub-module, but if using combine_with_exported_module!, it will + // be flattened and all functions registered at the top level. + pub mod my_sub_module { + pub fn get_sub_num() -> i64 { + 0 + } + } } // Define the package 'MyPackage'. @@ -96,8 +107,17 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { // Merge all registered functions and constants from the plugin module into the custom package. // + // Functions in the sub-module 'my_sub_module' are flattened and registered at the top level + // instead of in a sub-module. + // // 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. + // + // This call ends up registering three functions at the top level of the package: + // 1) greet + // 2) get_num + // 3) get_sub_num (flattened from sub-module 'my_sub_module') + // combine_with_exported_module!(module, "my-functions", my_module)); }); ``` From 52298bd96c2ee6b357ef69aba6f58cd530594f5e Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 14 Sep 2020 11:40:15 +0800 Subject: [PATCH 08/34] Streamline rhai_generate. --- codegen/src/lib.rs | 2 +- codegen/src/rhai_module.rs | 22 +++++++++++++--------- 2 files changed, 14 insertions(+), 10 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 3763b47e..4426f173 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -161,7 +161,7 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro Err(e) => return e.to_compile_error().into(), }; let tokens = quote! { - #module_path::rhai_generate_into_module(#module_expr); + #module_path::rhai_generate_into_module(#module_expr, true); }; proc_macro::TokenStream::from(tokens) } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 165f7438..66f23390 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -17,7 +17,7 @@ pub(crate) fn generate_body( let mut set_fn_stmts: Vec = Vec::new(); let mut set_const_stmts: Vec = Vec::new(); let mut add_mod_blocks: Vec = Vec::new(); - let mut set_mod_blocks: Vec = Vec::new(); + let mut set_flattened_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); for (const_name, const_type, const_expr) in consts { @@ -55,10 +55,10 @@ pub(crate) fn generate_body( }) .unwrap(), ); - set_mod_blocks.push( + set_flattened_mod_blocks.push( syn::parse2::(quote! { #(#cfg_attrs)* { - self::#module_name::rhai_generate_into_module(m); + self::#module_name::rhai_generate_into_module(m, flatten); } }) .unwrap(), @@ -138,18 +138,22 @@ pub(crate) fn generate_body( pub mod generate_info { #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - #(#set_fn_stmts)* - #(#set_const_stmts)* - #(#add_mod_blocks)* + rhai_generate_into_module(&mut m, false); m } - pub fn rhai_generate_into_module(m: &mut Module) { + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { #(#set_fn_stmts)* #(#set_const_stmts)* - #(#set_mod_blocks)* + + if flatten { + #(#set_flattened_mod_blocks)* + } else { + #(#add_mod_blocks)* + } } } }) From c63f30a6a23c11ca261550c0140dca3ebf760496 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 14 Sep 2020 11:40:23 +0800 Subject: [PATCH 09/34] Fix tests. --- codegen/src/test/function.rs | 17 +- codegen/src/test/module.rs | 508 ++++++++++++++++++++++++++++------- xxx.rs | 0 3 files changed, 424 insertions(+), 101 deletions(-) delete mode 100644 xxx.rs diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 5430b311..7e22ea88 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -237,11 +237,14 @@ mod generate_tests { let expected = expected.to_string(); if &actual != &expected { let mut counter = 0; - let iter = actual - .chars() - .zip(expected.chars()) - .inspect(|_| counter += 1) - .skip_while(|(a, e)| *a == *e); + let iter = actual.chars().zip(expected.chars()).skip_while(|(a, e)| { + if *a == *e { + counter += 1; + true + } else { + false + } + }); let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); @@ -252,8 +255,10 @@ mod generate_tests { (actual_diff, expected_diff) }; eprintln!("actual != expected, diverge at char {}", counter); + eprintln!(" actual: {}", actual_diff); + eprintln!("expected: {}", expected_diff); + assert!(false); } - assert_eq!(actual, expected); } #[test] diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 677cd040..5e9b5433 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -218,12 +218,15 @@ mod generate_tests { let expected = expected.to_string(); if &actual != &expected { let mut counter = 0; - let iter = actual - .chars() - .zip(expected.chars()) - .inspect(|_| counter += 1) - .skip_while(|(a, e)| *a == *e); - let (_actual_diff, _expected_diff) = { + let iter = actual.chars().zip(expected.chars()).skip_while(|(a, e)| { + if *a == *e { + counter += 1; + true + } else { + false + } + }); + let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); for (a, e) in iter.take(50) { @@ -233,8 +236,11 @@ mod generate_tests { (actual_diff, expected_diff) }; eprintln!("actual != expected, diverge at char {}", counter); + eprintln!(" actual: {}", actual_diff); + eprintln!("expected: {}", expected_diff); + assert!(false); } - assert_eq!(actual, expected); + //assert_eq!(actual, expected); } #[test] @@ -244,14 +250,20 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod empty { #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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) { + if flatten {} else {} + } } }; @@ -270,18 +282,24 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn get_mystic_number() -> INT { 42 } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("get_mystic_number", FnAccess::Public, &[], CallableFunction::from_plugin(get_mystic_number_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct get_mystic_number_token(); @@ -327,18 +345,24 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn add_one_to(x: INT) -> INT { x + 1 } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(add_one_to_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct add_one_to_token(); @@ -391,6 +415,7 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod two_fns { pub fn add_one_to(x: INT) -> INT { x + 1 @@ -402,17 +427,21 @@ mod generate_tests { #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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_n", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(add_one_to_token())); m.set_fn("add_n", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(add_n_to_token())); - m + if flatten {} else {} } - #[allow(non_camel_case_types)] struct add_one_to_token(); impl PluginFunction for add_one_to_token { @@ -488,19 +517,25 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn add_together(x: INT, y: INT) -> INT { x + y } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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_together", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(add_together_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct add_together_token(); @@ -550,15 +585,21 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn add_together(x: INT, y: INT) -> INT { x + y } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(add_together_token())); @@ -568,7 +609,7 @@ mod generate_tests { m.set_fn("add_together", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(add_together_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct add_together_token(); @@ -606,6 +647,44 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_constant_type_module() { + let input_tokens: TokenStream = quote! { + pub mod one_constant { + #[derive(Debug, Clone)] + pub struct Foo(pub INT); + + pub const MYSTIC_NUMBER: Foo = Foo(42); + } + }; + + let expected_tokens = quote! { + #[allow(dead_code)] + pub mod one_constant { + #[derive(Debug, Clone)] + pub struct Foo(pub INT); + + pub const MYSTIC_NUMBER: Foo = Foo(42); + #[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_var("MYSTIC_NUMBER", (Foo(42)) as Foo); + if flatten {} else {} + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_constant_module() { let input_tokens: TokenStream = quote! { @@ -615,16 +694,22 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_constant { pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("MYSTIC_NUMBER", 42); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("MYSTIC_NUMBER", (42) as INT); + if flatten {} else {} + } } }; @@ -642,17 +727,23 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_constant { pub use rhai::INT; pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("MYSTIC_NUMBER", 42); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("MYSTIC_NUMBER", (42) as INT); + if flatten {} else {} + } } }; @@ -671,17 +762,23 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { fn get_mystic_number() -> INT { 42 } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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) { + if flatten {} else {} + } } }; @@ -701,17 +798,23 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn get_mystic_number() -> INT { 42 } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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) { + if flatten {} else {} + } } }; @@ -734,6 +837,7 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn get_mystic_number() -> INT { 42 @@ -743,12 +847,17 @@ mod generate_tests { } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("get_mystic_number", FnAccess::Public, &[], CallableFunction::from_plugin(get_mystic_number_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct get_mystic_number_token(); @@ -792,15 +901,21 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_constant { const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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) { + if flatten {} else {} + } } }; @@ -819,19 +934,25 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod str_fn { pub fn print_out_to(x: &str) { x + 1 } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("print_out_to", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(print_out_to_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct print_out_to_token(); @@ -878,19 +999,25 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod ref_fn { pub fn increment(x: &mut FLOAT) { *x += 1.0 as FLOAT; } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("increment", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(increment_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct increment_token(); @@ -939,20 +1066,27 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { + #[allow(dead_code)] pub mod it_is { pub fn increment(x: &mut FLOAT) { *x += 1.0 as FLOAT; } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("increment", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(increment_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct increment_token(); @@ -984,12 +1118,20 @@ mod generate_tests { } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - { m.set_sub_module("it_is", self::it_is::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + if flatten { + { self::it_is::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("it_is", self::it_is::rhai_module_generate()); } + } + } } }; @@ -1011,21 +1153,28 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { #[cfg(not(feature = "no_float"))] + #[allow(dead_code)] pub mod it_is { pub fn increment(x: &mut FLOAT) { *x += 1.0 as FLOAT; } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("increment", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(increment_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct increment_token(); @@ -1057,14 +1206,24 @@ mod generate_tests { } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - #[cfg(not(feature = "no_float"))] { - m.set_sub_module("it_is", self::it_is::rhai_module_generate()); - } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + if flatten { + #[cfg(not(feature = "no_float"))] { + self::it_is::rhai_generate_into_module(m, flatten); + } + } else { + #[cfg(not(feature = "no_float"))] { + m.set_sub_module("it_is", self::it_is::rhai_module_generate()); + } + } + } } }; @@ -1084,18 +1243,24 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64) -> u64 { (*x) * (*x) } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("get$square", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(int_foo_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct int_foo_token(); @@ -1143,20 +1308,26 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64) -> u64 { (*x) * (*x) } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("square", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(int_foo_token())); m.set_fn("get$square", FnAccess::Public, &[core::any::TypeId::of::()], CallableFunction::from_plugin(int_foo_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct int_foo_token(); @@ -1204,20 +1375,26 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64, y: u64) { *x = y * y } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("set$squared", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(int_foo_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct int_foo_token(); @@ -1266,15 +1443,21 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64, y: u64) { *x = y * y } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("set_sq", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1283,7 +1466,7 @@ mod generate_tests { &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(int_foo_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct int_foo_token(); @@ -1332,20 +1515,26 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_index_fn { pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { x.get(i) } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("index$get$", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(get_by_index_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct get_by_index_token(); @@ -1395,15 +1584,21 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_index_fn { pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { x.get(i) } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("get", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::()], @@ -1412,7 +1607,7 @@ mod generate_tests { &[core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(get_by_index_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct get_by_index_token(); @@ -1462,21 +1657,27 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_index_fn { pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { x.entry(i).set(item) } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("index$set$", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(set_by_index_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct set_by_index_token(); @@ -1528,15 +1729,21 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_index_fn { pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { x.entry(i).set(item) } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + 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("set", FnAccess::Public, &[core::any::TypeId::of::(), core::any::TypeId::of::(), @@ -1547,7 +1754,7 @@ mod generate_tests { core::any::TypeId::of::(), core::any::TypeId::of::()], CallableFunction::from_plugin(set_by_index_token())); - m + if flatten {} else {} } #[allow(non_camel_case_types)] struct set_by_index_token(); @@ -1598,26 +1805,41 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod one_constant { + #[allow(dead_code)] pub mod it_is { pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("MYSTIC_NUMBER", 42); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("MYSTIC_NUMBER", (42) as INT); + if flatten {} else {} + } } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - { m.set_sub_module("it_is", self::it_is::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + if flatten { + { self::it_is::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("it_is", self::it_is::rhai_module_generate()); } + } + } } }; @@ -1639,38 +1861,60 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod two_constants { + #[allow(dead_code)] pub mod first_is { pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("MYSTIC_NUMBER", 42); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("MYSTIC_NUMBER", (42) as INT); + if flatten {} else {} + } } + #[allow(dead_code)] pub mod second_is { pub const SPECIAL_CPU_NUMBER: INT = 68000; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("SPECIAL_CPU_NUMBER", 68000); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("SPECIAL_CPU_NUMBER", (68000) as INT); + if flatten {} else {} + } } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - { m.set_sub_module("first_is", self::first_is::rhai_module_generate()); } - { m.set_sub_module("second_is", self::second_is::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + if flatten { + { self::first_is::rhai_generate_into_module(m, flatten); } + { self::second_is::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("first_is", self::first_is::rhai_module_generate()); } + { m.set_sub_module("second_is", self::second_is::rhai_module_generate()); } + } + } } }; @@ -1711,112 +1955,186 @@ mod generate_tests { }; let expected_tokens = quote! { + #[allow(dead_code)] pub mod heap_root { pub const VALUE: INT = 100; + #[allow(dead_code)] pub mod left { pub const VALUE: INT = 19; + #[allow(dead_code)] pub mod left { pub const VALUE: INT = 17; + #[allow(dead_code)] pub mod left { pub const VALUE: INT = 2; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 2); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (2) as INT); + if flatten {} else {} + } } + #[allow(dead_code)] pub mod right { pub const VALUE: INT = 7; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 7); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (7) as INT); + if flatten {} else {} + } } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 17); - { m.set_sub_module("left", self::left::rhai_module_generate()); } - { m.set_sub_module("right", self::right::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (17) as INT); + + if flatten { + { self::left::rhai_generate_into_module(m, flatten); } + { self::right::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("left", self::left::rhai_module_generate()); } + { m.set_sub_module("right", self::right::rhai_module_generate()); } + } + } } + #[allow(dead_code)] pub mod right { pub const VALUE: INT = 3; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 3); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (3) as INT); + if flatten {} else {} + } } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 19); - { m.set_sub_module("left", self::left::rhai_module_generate()); } - { m.set_sub_module("right", self::right::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (19) as INT); + + if flatten { + { self::left::rhai_generate_into_module(m, flatten); } + { self::right::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("left", self::left::rhai_module_generate()); } + { m.set_sub_module("right", self::right::rhai_module_generate()); } + } + } } + #[allow(dead_code)] pub mod right { pub const VALUE: INT = 36; + #[allow(dead_code)] pub mod left { pub const VALUE: INT = 25; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 25); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (25) as INT); + if flatten {} else {} + } } + #[allow(dead_code)] pub mod right { pub const VALUE: INT = 1; #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 1); + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (1) as INT); + if flatten {} else {} + } } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 36); - { m.set_sub_module("left", self::left::rhai_module_generate()); } - { m.set_sub_module("right", self::right::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (36) as INT); + + if flatten { + { self::left::rhai_generate_into_module(m, flatten); } + { self::right::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("left", self::left::rhai_module_generate()); } + { m.set_sub_module("right", self::right::rhai_module_generate()); } + } + } } #[allow(unused_imports)] use super::*; - #[allow(unused_mut)] + pub fn rhai_module_generate() -> Module { let mut m = Module::new(); - m.set_var("VALUE", 100); - { m.set_sub_module("left", self::left::rhai_module_generate()); } - { m.set_sub_module("right", self::right::rhai_module_generate()); } + rhai_generate_into_module(&mut m, false); m } + #[allow(unused_mut)] + pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { + m.set_var("VALUE", (100) as INT); + + if flatten { + { self::left::rhai_generate_into_module(m, flatten); } + { self::right::rhai_generate_into_module(m, flatten); } + } else { + { m.set_sub_module("left", self::left::rhai_module_generate()); } + { m.set_sub_module("right", self::right::rhai_module_generate()); } + } + } } }; diff --git a/xxx.rs b/xxx.rs deleted file mode 100644 index e69de29b..00000000 From 7ceb624ec191c937c49a675af9d981ee0fd8a926 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:12:11 +0800 Subject: [PATCH 10/34] Add combine_with_exported_module. --- codegen/src/lib.rs | 2 +- codegen/src/rhai_module.rs | 11 ++++++++--- doc/src/rust/packages/create.md | 12 ------------ tests/plugins.rs | 2 +- xxx.rs | 0 5 files changed, 10 insertions(+), 17 deletions(-) create mode 100644 xxx.rs diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4426f173..3763b47e 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -161,7 +161,7 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro Err(e) => return e.to_compile_error().into(), }; let tokens = quote! { - #module_path::rhai_generate_into_module(#module_expr, true); + #module_path::rhai_generate_into_module(#module_expr); }; proc_macro::TokenStream::from(tokens) } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 66f23390..1661df43 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -17,7 +17,7 @@ pub(crate) fn generate_body( let mut set_fn_stmts: Vec = Vec::new(); let mut set_const_stmts: Vec = Vec::new(); let mut add_mod_blocks: Vec = Vec::new(); - let mut set_flattened_mod_blocks: Vec = Vec::new(); + let mut set_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); for (const_name, const_type, const_expr) in consts { @@ -55,10 +55,10 @@ pub(crate) fn generate_body( }) .unwrap(), ); - set_flattened_mod_blocks.push( + set_mod_blocks.push( syn::parse2::(quote! { #(#cfg_attrs)* { - self::#module_name::rhai_generate_into_module(m, flatten); + self::#module_name::rhai_generate_into_module(m); } }) .unwrap(), @@ -155,6 +155,11 @@ pub(crate) fn generate_body( #(#add_mod_blocks)* } } + pub fn rhai_generate_into_module(m: &mut Module) { + #(#set_fn_stmts)* + #(#set_const_stmts)* + #(#set_mod_blocks)* + } } }) .unwrap(); diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 53ac17a0..c4ee27e4 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -66,9 +66,6 @@ In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage `rhai::plugins::combine_with_exported_module!` adds all functions and constants from the [plugins][plugin module] definition into the package itself. -All sub-modules are _flattened_ (i.e. all functions and constants defined within sub-modules are registered -at the top level) and so there will not be any sub-modules added to the package. - ```rust // Import necessary types and traits. use rhai::{ @@ -107,17 +104,8 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { // Merge all registered functions and constants from the plugin module into the custom package. // - // Functions in the sub-module 'my_sub_module' are flattened and registered at the top level - // instead of in a sub-module. - // // 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. - // - // This call ends up registering three functions at the top level of the package: - // 1) greet - // 2) get_num - // 3) get_sub_num (flattened from sub-module 'my_sub_module') - // combine_with_exported_module!(module, "my-functions", my_module)); }); ``` diff --git a/tests/plugins.rs b/tests/plugins.rs index 2bee84e4..928f0381 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -11,7 +11,7 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; - pub const MYSTIC_NUMBER: INT = 42; + pub const MYSTIC_NUMBER: INT = 42 as INT; #[cfg(not(feature = "no_object"))] pub mod feature { diff --git a/xxx.rs b/xxx.rs new file mode 100644 index 00000000..e69de29b From d1d3aaf374db6e604523d180eb22f183211f6863 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 22:36:24 +0800 Subject: [PATCH 11/34] Add cast to type for constants to avoid mis-typing. --- tests/plugins.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/plugins.rs b/tests/plugins.rs index 928f0381..2bee84e4 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -11,7 +11,7 @@ mod test { pub mod special_array_package { use rhai::{Array, INT}; - pub const MYSTIC_NUMBER: INT = 42 as INT; + pub const MYSTIC_NUMBER: INT = 42; #[cfg(not(feature = "no_object"))] pub mod feature { From 1060ff35fd9a2c42173ddbbe1408ddb72a4bc8e6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 13 Sep 2020 23:13:07 +0800 Subject: [PATCH 12/34] Fix tests. --- doc/src/rust/packages/create.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index c4ee27e4..53ac17a0 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -66,6 +66,9 @@ In fact, this exactly is how Rhai's built-in packages, such as `BasicMathPackage `rhai::plugins::combine_with_exported_module!` adds all functions and constants from the [plugins][plugin module] definition into the package itself. +All sub-modules are _flattened_ (i.e. all functions and constants defined within sub-modules are registered +at the top level) and so there will not be any sub-modules added to the package. + ```rust // Import necessary types and traits. use rhai::{ @@ -104,8 +107,17 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { // Merge all registered functions and constants from the plugin module into the custom package. // + // Functions in the sub-module 'my_sub_module' are flattened and registered at the top level + // instead of in a sub-module. + // // 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. + // + // This call ends up registering three functions at the top level of the package: + // 1) greet + // 2) get_num + // 3) get_sub_num (flattened from sub-module 'my_sub_module') + // combine_with_exported_module!(module, "my-functions", my_module)); }); ``` From 3dc599c93601950e7837d486e86b110e4558ff2d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 14 Sep 2020 11:40:15 +0800 Subject: [PATCH 13/34] Streamline rhai_generate. --- codegen/src/lib.rs | 2 +- codegen/src/rhai_module.rs | 11 +++-------- 2 files changed, 4 insertions(+), 9 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 3763b47e..4426f173 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -161,7 +161,7 @@ pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro Err(e) => return e.to_compile_error().into(), }; let tokens = quote! { - #module_path::rhai_generate_into_module(#module_expr); + #module_path::rhai_generate_into_module(#module_expr, true); }; proc_macro::TokenStream::from(tokens) } diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 1661df43..66f23390 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -17,7 +17,7 @@ pub(crate) fn generate_body( let mut set_fn_stmts: Vec = Vec::new(); let mut set_const_stmts: Vec = Vec::new(); let mut add_mod_blocks: Vec = Vec::new(); - let mut set_mod_blocks: Vec = Vec::new(); + let mut set_flattened_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); for (const_name, const_type, const_expr) in consts { @@ -55,10 +55,10 @@ pub(crate) fn generate_body( }) .unwrap(), ); - set_mod_blocks.push( + set_flattened_mod_blocks.push( syn::parse2::(quote! { #(#cfg_attrs)* { - self::#module_name::rhai_generate_into_module(m); + self::#module_name::rhai_generate_into_module(m, flatten); } }) .unwrap(), @@ -155,11 +155,6 @@ pub(crate) fn generate_body( #(#add_mod_blocks)* } } - pub fn rhai_generate_into_module(m: &mut Module) { - #(#set_fn_stmts)* - #(#set_const_stmts)* - #(#set_mod_blocks)* - } } }) .unwrap(); From c692cd7e359e74fbf7602431c7e615fec98ead27 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Mon, 14 Sep 2020 11:40:23 +0800 Subject: [PATCH 14/34] Fix tests. --- xxx.rs | 0 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 xxx.rs diff --git a/xxx.rs b/xxx.rs deleted file mode 100644 index e69de29b..00000000 From 5e4ba9c0163cdf6b6b26eb6a7d934bdb47444eef Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 17 Sep 2020 11:37:23 +0800 Subject: [PATCH 15/34] Swich back to assert_eq! for token stream tests. --- codegen/src/test/function.rs | 5 ++++- codegen/src/test/module.rs | 6 ++++-- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 7e22ea88..a8ded0c0 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -245,6 +245,8 @@ mod generate_tests { false } }); + eprintln!("actual != expected, diverge at char {}", counter); + /* let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); @@ -254,11 +256,12 @@ mod generate_tests { } (actual_diff, expected_diff) }; - eprintln!("actual != expected, diverge at char {}", counter); eprintln!(" actual: {}", actual_diff); eprintln!("expected: {}", expected_diff); assert!(false); + */ } + assert_eq!(actual, expected); } #[test] diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 5e9b5433..6743b6db 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -226,6 +226,8 @@ mod generate_tests { false } }); + eprintln!("actual != expected, diverge at char {}", counter); + /* let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); @@ -235,12 +237,12 @@ mod generate_tests { } (actual_diff, expected_diff) }; - eprintln!("actual != expected, diverge at char {}", counter); eprintln!(" actual: {}", actual_diff); eprintln!("expected: {}", expected_diff); assert!(false); + */ } - //assert_eq!(actual, expected); + assert_eq!(actual, expected); } #[test] From c07c4561eb8cd74e6ce28ad4fb948b2264ed2b28 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 17 Sep 2020 11:40:31 +0800 Subject: [PATCH 16/34] Remove unused_variables. --- codegen/src/lib.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 4426f173..5da8e1b4 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -154,8 +154,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke #[proc_macro] pub fn combine_with_exported_module(args: proc_macro::TokenStream) -> proc_macro::TokenStream { - #[allow(unused_variables)] - let (module_expr, export_name, module_path) = match crate::register::parse_register_macro(args) + let (module_expr, _export_name, module_path) = match crate::register::parse_register_macro(args) { Ok(triple) => triple, Err(e) => return e.to_compile_error().into(), From 92be487e8072382edd58c580bcb2b05118dbade0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Thu, 17 Sep 2020 11:56:10 +0800 Subject: [PATCH 17/34] Implement constants via calling the constant. --- codegen/src/module.rs | 1 - codegen/src/rhai_module.rs | 5 ++- codegen/src/test/function.rs | 4 +- codegen/src/test/module.rs | 76 +++++++++--------------------------- 4 files changed, 23 insertions(+), 63 deletions(-) diff --git a/codegen/src/module.rs b/codegen/src/module.rs index 4b829693..85b1278c 100644 --- a/codegen/src/module.rs +++ b/codegen/src/module.rs @@ -282,7 +282,6 @@ impl Module { // Regenerate the module with the new content added. Ok(quote! { #(#mod_attrs)* - #[allow(dead_code)] pub mod #mod_name { #(#orig_content)* #(#inner_modules)* diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 66f23390..1859ba42 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -20,11 +20,12 @@ pub(crate) fn generate_body( let mut set_flattened_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); - for (const_name, const_type, const_expr) in consts { + for (const_name, _, _) in consts { let const_literal = syn::LitStr::new(&const_name, proc_macro2::Span::call_site()); + let const_ref = syn::Ident::new(&const_name, proc_macro2::Span::call_site()); set_const_stmts.push( syn::parse2::(quote! { - m.set_var(#const_literal, (#const_expr) as #const_type); + m.set_var(#const_literal, #const_ref); }) .unwrap(), ); diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index a8ded0c0..a4a54959 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -237,7 +237,7 @@ mod generate_tests { let expected = expected.to_string(); if &actual != &expected { let mut counter = 0; - let iter = actual.chars().zip(expected.chars()).skip_while(|(a, e)| { + let _iter = actual.chars().zip(expected.chars()).skip_while(|(a, e)| { if *a == *e { counter += 1; true @@ -250,7 +250,7 @@ mod generate_tests { let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); - for (a, e) in iter.take(50) { + for (a, e) in _iter.take(50) { actual_diff.push(a); expected_diff.push(e); } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 6743b6db..b560e94b 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -218,7 +218,7 @@ mod generate_tests { let expected = expected.to_string(); if &actual != &expected { let mut counter = 0; - let iter = actual.chars().zip(expected.chars()).skip_while(|(a, e)| { + let _iter = actual.chars().zip(expected.chars()).skip_while(|(a, e)| { if *a == *e { counter += 1; true @@ -231,7 +231,7 @@ mod generate_tests { let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); - for (a, e) in iter.take(50) { + for (a, e) in _iter.take(50) { actual_diff.push(a); expected_diff.push(e); } @@ -252,7 +252,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod empty { #[allow(unused_imports)] use super::*; @@ -284,7 +283,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn get_mystic_number() -> INT { 42 @@ -347,7 +345,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn add_one_to(x: INT) -> INT { x + 1 @@ -417,7 +414,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod two_fns { pub fn add_one_to(x: INT) -> INT { x + 1 @@ -519,7 +515,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn add_together(x: INT, y: INT) -> INT { x + y @@ -587,7 +582,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn add_together(x: INT, y: INT) -> INT { x + y @@ -661,7 +655,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_constant { #[derive(Debug, Clone)] pub struct Foo(pub INT); @@ -677,7 +670,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("MYSTIC_NUMBER", (Foo(42)) as Foo); + m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER); if flatten {} else {} } } @@ -696,7 +689,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_constant { pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] @@ -709,7 +701,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("MYSTIC_NUMBER", (42) as INT); + m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER); if flatten {} else {} } } @@ -729,7 +721,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_constant { pub use rhai::INT; pub const MYSTIC_NUMBER: INT = 42; @@ -743,7 +734,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("MYSTIC_NUMBER", (42) as INT); + m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER); if flatten {} else {} } } @@ -764,7 +755,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { fn get_mystic_number() -> INT { 42 @@ -800,7 +790,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn get_mystic_number() -> INT { 42 @@ -839,7 +828,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn get_mystic_number() -> INT { 42 @@ -903,7 +891,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_constant { const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] @@ -936,7 +923,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod str_fn { pub fn print_out_to(x: &str) { x + 1 @@ -1001,7 +987,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod ref_fn { pub fn increment(x: &mut FLOAT) { *x += 1.0 as FLOAT; @@ -1068,9 +1053,7 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { - #[allow(dead_code)] pub mod it_is { pub fn increment(x: &mut FLOAT) { *x += 1.0 as FLOAT; @@ -1155,10 +1138,9 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { #[cfg(not(feature = "no_float"))] - #[allow(dead_code)] + pub mod it_is { pub fn increment(x: &mut FLOAT) { *x += 1.0 as FLOAT; @@ -1245,7 +1227,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64) -> u64 { (*x) * (*x) @@ -1310,7 +1291,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64) -> u64 { (*x) * (*x) @@ -1377,7 +1357,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64, y: u64) { *x = y * y @@ -1445,7 +1424,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_fn { pub fn int_foo(x: &mut u64, y: u64) { *x = y * y @@ -1517,7 +1495,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_index_fn { pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { x.get(i) @@ -1586,7 +1563,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_index_fn { pub fn get_by_index(x: &mut MyCollection, i: u64) -> FLOAT { x.get(i) @@ -1659,7 +1635,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_index_fn { pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { x.entry(i).set(item) @@ -1731,7 +1706,6 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_index_fn { pub fn set_by_index(x: &mut MyCollection, i: u64, item: FLOAT) { x.entry(i).set(item) @@ -1807,9 +1781,7 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod one_constant { - #[allow(dead_code)] pub mod it_is { pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] @@ -1822,7 +1794,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("MYSTIC_NUMBER", (42) as INT); + m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER); if flatten {} else {} } } @@ -1863,9 +1835,7 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod two_constants { - #[allow(dead_code)] pub mod first_is { pub const MYSTIC_NUMBER: INT = 42; #[allow(unused_imports)] @@ -1878,11 +1848,10 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("MYSTIC_NUMBER", (42) as INT); + m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER); if flatten {} else {} } } - #[allow(dead_code)] pub mod second_is { pub const SPECIAL_CPU_NUMBER: INT = 68000; #[allow(unused_imports)] @@ -1895,7 +1864,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("SPECIAL_CPU_NUMBER", (68000) as INT); + m.set_var("SPECIAL_CPU_NUMBER", SPECIAL_CPU_NUMBER); if flatten {} else {} } } @@ -1957,16 +1926,12 @@ mod generate_tests { }; let expected_tokens = quote! { - #[allow(dead_code)] pub mod heap_root { pub const VALUE: INT = 100; - #[allow(dead_code)] pub mod left { pub const VALUE: INT = 19; - #[allow(dead_code)] pub mod left { pub const VALUE: INT = 17; - #[allow(dead_code)] pub mod left { pub const VALUE: INT = 2; #[allow(unused_imports)] @@ -1979,11 +1944,10 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (2) as INT); + m.set_var("VALUE", VALUE); if flatten {} else {} } } - #[allow(dead_code)] pub mod right { pub const VALUE: INT = 7; #[allow(unused_imports)] @@ -1996,7 +1960,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (7) as INT); + m.set_var("VALUE", VALUE); if flatten {} else {} } } @@ -2010,7 +1974,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (17) as INT); + m.set_var("VALUE", VALUE); if flatten { { self::left::rhai_generate_into_module(m, flatten); } @@ -2021,7 +1985,6 @@ mod generate_tests { } } } - #[allow(dead_code)] pub mod right { pub const VALUE: INT = 3; #[allow(unused_imports)] @@ -2034,7 +1997,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (3) as INT); + m.set_var("VALUE", VALUE); if flatten {} else {} } } @@ -2048,7 +2011,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (19) as INT); + m.set_var("VALUE", VALUE); if flatten { { self::left::rhai_generate_into_module(m, flatten); } @@ -2059,10 +2022,8 @@ mod generate_tests { } } } - #[allow(dead_code)] pub mod right { pub const VALUE: INT = 36; - #[allow(dead_code)] pub mod left { pub const VALUE: INT = 25; #[allow(unused_imports)] @@ -2075,11 +2036,10 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (25) as INT); + m.set_var("VALUE", VALUE); if flatten {} else {} } } - #[allow(dead_code)] pub mod right { pub const VALUE: INT = 1; #[allow(unused_imports)] @@ -2092,7 +2052,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (1) as INT); + m.set_var("VALUE", VALUE); if flatten {} else {} } } @@ -2106,7 +2066,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (36) as INT); + m.set_var("VALUE", VALUE); if flatten { { self::left::rhai_generate_into_module(m, flatten); } @@ -2127,7 +2087,7 @@ mod generate_tests { } #[allow(unused_mut)] pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) { - m.set_var("VALUE", (100) as INT); + m.set_var("VALUE", VALUE); if flatten { { self::left::rhai_generate_into_module(m, flatten); } From b795ce9f45f1344f37f3bdcb46210e8c51cb95b4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 11:52:23 +0800 Subject: [PATCH 18/34] Refine docs. --- doc/src/rust/packages/index.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index ee0daa37..a5df94cd 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -29,7 +29,7 @@ engine.load_package(package.get()); // load the package manually. 'get' returns Difference Between a Package and a Module ---------------------------------------- -Packages are actually implemented as [modules], so they share a lot of behavior and characteristics. +Packages are internally implemented as [modules], so they share a lot of behavior and characteristics. The main difference is that a package loads under the _global_ namespace, while a module loads under its own namespace alias specified in an [`import`] statement (see also [modules]). @@ -37,6 +37,11 @@ namespace alias specified in an [`import`] statement (see also [modules]). A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with the `import` statement). +Functions in a package are _flattened_, meaning that functions from sub-modules must be pulled up to the root level. +In other words, there can be no sub-modules in a package, and everything should reside in one, flat namespace. + +Only functions matter for a package. Constant variables registered in a package are ignored. + Load a Module as a Package -------------------------- From 23b5bc4bd9c2a9333073938d529bd556bc0b2808 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 12:31:41 +0800 Subject: [PATCH 19/34] Prevent errors when using alternative error printout style. --- codegen/src/test/function.rs | 4 +++- codegen/src/test/module.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index a4a54959..31797438 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -245,7 +245,6 @@ mod generate_tests { false } }); - eprintln!("actual != expected, diverge at char {}", counter); /* let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); @@ -256,6 +255,9 @@ mod generate_tests { } (actual_diff, expected_diff) }; + */ + eprintln!("actual != expected, diverge at char {}", counter); + /* eprintln!(" actual: {}", actual_diff); eprintln!("expected: {}", expected_diff); assert!(false); diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index b560e94b..4d71f296 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -226,7 +226,6 @@ mod generate_tests { false } }); - eprintln!("actual != expected, diverge at char {}", counter); /* let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); @@ -237,6 +236,9 @@ mod generate_tests { } (actual_diff, expected_diff) }; + */ + eprintln!("actual != expected, diverge at char {}", counter); + /* eprintln!(" actual: {}", actual_diff); eprintln!("expected: {}", expected_diff); assert!(false); From fe718fea39e4c45c0858946671aa8b88f11ddfa2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 13:59:38 +0800 Subject: [PATCH 20/34] Remove Position parameter from plugin call function. --- codegen/src/function.rs | 214 +++++++++++++++++++++-------------- codegen/src/test/function.rs | 12 +- codegen/src/test/module.rs | 38 +++---- src/fn_call.rs | 4 +- src/plugin.rs | 5 +- 5 files changed, 157 insertions(+), 116 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 75812dbd..aa059013 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -84,71 +84,91 @@ impl ExportedParams for ExportedFnParams { let mut skip = false; let mut special = FnSpecialAccess::None; for attr in attrs { - let crate::attrs::AttrItem { key, value, span: item_span } = attr; + let crate::attrs::AttrItem { + key, + value, + span: item_span, + } = attr; match (key.to_string().as_ref(), value) { ("get", None) | ("set", None) | ("name", None) => { return Err(syn::Error::new(key.span(), "requires value")) - }, + } ("name", Some(s)) if &s.value() == FN_IDX_GET => { - return Err(syn::Error::new(item_span, - "use attribute 'index_get' instead")) - }, + return Err(syn::Error::new( + item_span, + "use attribute 'index_get' instead", + )) + } ("name", Some(s)) if &s.value() == FN_IDX_SET => { - return Err(syn::Error::new(item_span, - "use attribute 'index_set' instead")) - }, + return Err(syn::Error::new( + item_span, + "use attribute 'index_set' instead", + )) + } ("name", Some(s)) if s.value().starts_with("get$") => { - return Err(syn::Error::new(item_span, - format!("use attribute 'getter = \"{}\"' instead", - &s.value()["get$".len()..]))) - }, + return Err(syn::Error::new( + item_span, + format!( + "use attribute 'getter = \"{}\"' instead", + &s.value()["get$".len()..] + ), + )) + } ("name", Some(s)) if s.value().starts_with("set$") => { - return Err(syn::Error::new(item_span, - format!("use attribute 'setter = \"{}\"' instead", - &s.value()["set$".len()..]))) - }, + return Err(syn::Error::new( + item_span, + format!( + "use attribute 'setter = \"{}\"' instead", + &s.value()["set$".len()..] + ), + )) + } ("name", Some(s)) if s.value().contains('$') => { - return Err(syn::Error::new(s.span(), - "Rhai function names may not contain dollar sign")) - }, + return Err(syn::Error::new( + s.span(), + "Rhai function names may not contain dollar sign", + )) + } ("name", Some(s)) if s.value().contains('.') => { - return Err(syn::Error::new(s.span(), - "Rhai function names may not contain dot")) - }, - ("name", Some(s)) => { - name.push(s.value()) - }, - ("set", Some(s)) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Property(Property::Set(syn::Ident::new(&s.value(), - s.span()))), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting setter")) + return Err(syn::Error::new( + s.span(), + "Rhai function names may not contain dot", + )) + } + ("name", Some(s)) => name.push(s.value()), + ("set", Some(s)) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Property(Property::Set( + syn::Ident::new(&s.value(), s.span()), + )), + _ => return Err(syn::Error::new(item_span.span(), "conflicting setter")), } - }, - ("get", Some(s)) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Property(Property::Get(syn::Ident::new(&s.value(), - s.span()))), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting getter")) + } + ("get", Some(s)) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Property(Property::Get( + syn::Ident::new(&s.value(), s.span()), + )), + _ => return Err(syn::Error::new(item_span.span(), "conflicting getter")), } - }, - ("index_get", None) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Index(Index::Get), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting index_get")) + } + ("index_get", None) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Index(Index::Get), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting index_get")) + } } - }, + } - ("index_set", None) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Index(Index::Set), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting index_set")) + ("index_set", None) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Index(Index::Set), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting index_set")) + } } - }, + } ("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")) @@ -327,26 +347,40 @@ impl ExportedFn { } pub(crate) fn exported_names(&self) -> Vec { - let mut literals = self.params.name.as_ref() - .map(|v| v.iter() - .map(|s| syn::LitStr::new(s, proc_macro2::Span::call_site())).collect()) + let mut literals = self + .params + .name + .as_ref() + .map(|v| { + v.iter() + .map(|s| syn::LitStr::new(s, proc_macro2::Span::call_site())) + .collect() + }) .unwrap_or_else(|| Vec::new()); match self.params.special { - FnSpecialAccess::None => {}, - FnSpecialAccess::Property(Property::Get(ref g)) => - literals.push(syn::LitStr::new(&format!("get${}", g.to_string()), g.span())), - FnSpecialAccess::Property(Property::Set(ref s)) => - literals.push(syn::LitStr::new(&format!("set${}", s.to_string()), s.span())), - FnSpecialAccess::Index(Index::Get) => - literals.push(syn::LitStr::new(FN_IDX_GET, proc_macro2::Span::call_site())), - FnSpecialAccess::Index(Index::Set) => - literals.push(syn::LitStr::new(FN_IDX_SET, proc_macro2::Span::call_site())), + FnSpecialAccess::None => {} + FnSpecialAccess::Property(Property::Get(ref g)) => literals.push(syn::LitStr::new( + &format!("get${}", g.to_string()), + g.span(), + )), + FnSpecialAccess::Property(Property::Set(ref s)) => literals.push(syn::LitStr::new( + &format!("set${}", s.to_string()), + s.span(), + )), + FnSpecialAccess::Index(Index::Get) => { + literals.push(syn::LitStr::new(FN_IDX_GET, proc_macro2::Span::call_site())) + } + FnSpecialAccess::Index(Index::Set) => { + literals.push(syn::LitStr::new(FN_IDX_SET, proc_macro2::Span::call_site())) + } } if literals.is_empty() { - literals.push(syn::LitStr::new(&self.signature.ident.to_string(), - self.signature.ident.span())); + literals.push(syn::LitStr::new( + &self.signature.ident.to_string(), + self.signature.ident.span(), + )); } literals @@ -394,53 +428,61 @@ impl ExportedFn { match params.special { // 2a. Property getters must take only the subject as an argument. - FnSpecialAccess::Property(Property::Get(_)) if self.arg_count() != 1 => + FnSpecialAccess::Property(Property::Get(_)) if self.arg_count() != 1 => { return Err(syn::Error::new( self.signature.span(), "property getter requires exactly 1 argument", - )), + )) + } // 2b. Property getters must return a value. - FnSpecialAccess::Property(Property::Get(_)) if self.return_type().is_none() => + FnSpecialAccess::Property(Property::Get(_)) if self.return_type().is_none() => { return Err(syn::Error::new( self.signature.span(), - "property getter must return a value" - )), + "property getter must return a value", + )) + } // 3a. Property setters must take the subject and a new value as arguments. - FnSpecialAccess::Property(Property::Set(_)) if self.arg_count() != 2 => + FnSpecialAccess::Property(Property::Set(_)) if self.arg_count() != 2 => { return Err(syn::Error::new( self.signature.span(), "property setter requires exactly 2 arguments", - )), + )) + } // 3b. Property setters must return nothing. - FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => + FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => { return Err(syn::Error::new( self.signature.span(), - "property setter must return no value" - )), + "property setter must return no value", + )) + } // 4a. Index getters must take the subject and the accessed "index" as arguments. - FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => + FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => { return Err(syn::Error::new( self.signature.span(), "index getter requires exactly 2 arguments", - )), + )) + } // 4b. Index getters must return a value. - FnSpecialAccess::Index(Index::Get) if self.return_type().is_none() => + FnSpecialAccess::Index(Index::Get) if self.return_type().is_none() => { return Err(syn::Error::new( self.signature.span(), - "index getter must return a value" - )), + "index getter must return a value", + )) + } // 5a. Index setters must take the subject, "index", and new value as arguments. - FnSpecialAccess::Index(Index::Set) if self.arg_count() != 3 => + FnSpecialAccess::Index(Index::Set) if self.arg_count() != 3 => { return Err(syn::Error::new( self.signature.span(), "index setter requires exactly 3 arguments", - )), + )) + } // 5b. Index setters must return nothing. - FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => + FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => { return Err(syn::Error::new( self.signature.span(), - "index setter must return no value" - )), + "index setter must return no value", + )) + } _ => {} } @@ -685,7 +727,7 @@ impl ExportedFn { quote! { impl PluginFunction for #type_name { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), #arg_count, "wrong arg count: {} != {}", diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 31797438..61c1f942 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -279,7 +279,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 0usize, "wrong arg count: {} != {}", args.len(), 0usize); @@ -323,7 +323,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -364,7 +364,7 @@ mod generate_tests { let expected_tokens = quote! { impl PluginFunction for MyType { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -398,7 +398,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -445,7 +445,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -493,7 +493,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 4d71f296..59e58ce7 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -307,7 +307,7 @@ mod generate_tests { struct get_mystic_number_token(); impl PluginFunction for get_mystic_number_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 0usize, "wrong arg count: {} != {}", args.len(), 0usize); @@ -369,7 +369,7 @@ mod generate_tests { struct add_one_to_token(); impl PluginFunction for add_one_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -446,7 +446,7 @@ mod generate_tests { struct add_one_to_token(); impl PluginFunction for add_one_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -474,7 +474,7 @@ mod generate_tests { struct add_n_to_token(); impl PluginFunction for add_n_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -540,7 +540,7 @@ mod generate_tests { struct add_together_token(); impl PluginFunction for add_together_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -613,7 +613,7 @@ mod generate_tests { struct add_together_token(); impl PluginFunction for add_together_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -855,7 +855,7 @@ mod generate_tests { struct get_mystic_number_token(); impl PluginFunction for get_mystic_number_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 0usize, "wrong arg count: {} != {}", args.len(), 0usize); @@ -948,7 +948,7 @@ mod generate_tests { struct print_out_to_token(); impl PluginFunction for print_out_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1012,7 +1012,7 @@ mod generate_tests { struct increment_token(); impl PluginFunction for increment_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1079,7 +1079,7 @@ mod generate_tests { struct increment_token(); impl PluginFunction for increment_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1166,7 +1166,7 @@ mod generate_tests { struct increment_token(); impl PluginFunction for increment_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1251,7 +1251,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1317,7 +1317,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1383,7 +1383,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1454,7 +1454,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1521,7 +1521,7 @@ mod generate_tests { struct get_by_index_token(); impl PluginFunction for get_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1593,7 +1593,7 @@ mod generate_tests { struct get_by_index_token(); impl PluginFunction for get_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1662,7 +1662,7 @@ mod generate_tests { struct set_by_index_token(); impl PluginFunction for set_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 3usize, "wrong arg count: {} != {}", args.len(), 3usize); @@ -1738,7 +1738,7 @@ mod generate_tests { struct set_by_index_token(); impl PluginFunction for set_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 3usize, "wrong arg count: {} != {}", args.len(), 3usize); diff --git a/src/fn_call.rs b/src/fn_call.rs index 3728d4e8..c40d352f 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -232,7 +232,7 @@ impl Engine { // Run external function let result = if func.is_plugin_fn() { - func.get_plugin_fn().call(args, Position::none()) + func.get_plugin_fn().call(args) } else { func.get_native_fn()(self, lib, args) }; @@ -1099,7 +1099,7 @@ impl Engine { self.call_script_fn(scope, mods, state, lib, &mut None, name, func, args, level) } - Some(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut(), Position::none()), + Some(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut()), Some(f) if f.is_native() => { if !f.is_method() { // Clone first argument diff --git a/src/plugin.rs b/src/plugin.rs index ef4a6590..e6e4e956 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -3,7 +3,7 @@ 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, Position, RegisterResultFn, + Engine, EvalAltResult, FnAccess, ImmutableString, Module, RegisterResultFn, }; #[cfg(not(features = "no_module"))] @@ -34,8 +34,7 @@ pub trait PluginFunction { fn is_method_call(&self) -> bool; fn is_varadic(&self) -> bool; - fn call(&self, args: &mut [&mut Dynamic], pos: Position) - -> Result>; + fn call(&self, args: &mut [&mut Dynamic]) -> Result>; fn clone_boxed(&self) -> Box; From f5d132d1f4a613aa7b0c481c9dc8cceaf4a1c32d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 14:12:57 +0800 Subject: [PATCH 21/34] Fix doc test. --- src/fn_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fn_register.rs b/src/fn_register.rs index 28d33198..12aedad2 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -46,7 +46,7 @@ pub trait RegisterPlugin { /// fn is_method_call(&self) -> bool { false } /// fn is_varadic(&self) -> bool { false } /// - /// fn call(&self, args: &mut[&mut Dynamic], pos: Position) -> Result> { + /// fn call(&self, args: &mut[&mut Dynamic]) -> Result> { /// let x1: NUMBER = std::mem::take(args[0]).clone().cast::(); /// let y1: NUMBER = std::mem::take(args[1]).clone().cast::(); /// let x2: NUMBER = std::mem::take(args[2]).clone().cast::(); From 2ff3a1fde5a34937cdf7cccbcc262b9d149933fb Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 19 Sep 2020 12:14:02 +0800 Subject: [PATCH 22/34] Refine docs. --- doc/src/SUMMARY.md | 2 +- doc/src/engine/dsl.md | 14 ++++----- doc/src/language/dynamic.md | 4 +-- doc/src/language/eval.md | 8 ++--- doc/src/language/for.md | 3 ++ doc/src/language/modules/export.md | 8 ++--- doc/src/language/num-op.md | 2 +- doc/src/language/timestamps.md | 4 +-- doc/src/language/type-of.md | 4 +-- doc/src/patterns/config.md | 2 +- doc/src/patterns/control.md | 46 ++++++++++++++--------------- doc/src/patterns/oop.md | 8 ++--- doc/src/plugins/module.md | 45 ++++++++++++++++++++-------- doc/src/rust/modules/create.md | 6 ++-- doc/src/rust/packages/create.md | 21 +++++++------ doc/src/rust/packages/index.md | 2 +- doc/src/start/builds/performance.md | 25 +++++++++------- doc/src/start/features.md | 36 +++++++++++----------- 18 files changed, 133 insertions(+), 107 deletions(-) diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index db96844b..23f39757 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -52,7 +52,7 @@ The Rhai Scripting Language 1. [Comments](language/comments.md) 2. [Values and Types](language/values-and-types.md) 1. [Dynamic Values](language/dynamic.md) - 2. [type-of()](language/type-of.md) + 2. [type_of()](language/type-of.md) 3. [Numbers](language/numbers.md) 1. [Operators](language/num-op.md) 2. [Functions](language/num-fn.md) diff --git a/doc/src/engine/dsl.md b/doc/src/engine/dsl.md index 8f326ed1..593a7eb0 100644 --- a/doc/src/engine/dsl.md +++ b/doc/src/engine/dsl.md @@ -69,12 +69,12 @@ For example, the following is a SQL-like syntax for some obscure DSL operation: ```rust let table = [..., ..., ..., ...]; -// Syntax = calculate $ident$ $ident$ from $expr$ -> $ident$ : $expr$ -let total = calculate sum price from table -> row : row.weight > 50; +// Syntax = calculate $ident$ ( $expr$ -> $ident$ ) => $ident$ : $expr$ +let total = calculate sum(table->price) => row : row.weight > 50; // Note: There is nothing special about those symbols; to make it look exactly like SQL: -// Syntax = SELECT $ident$ ( $ident$ ) FROM $expr$ AS $ident$ WHERE $expr$ -let total = SELECT sum(price) FROM table AS row WHERE row.weight > 50; +// Syntax = SELECT $ident$ ( $ident$ ) AS $ident$ FROM $expr$ WHERE $expr$ +let total = SELECT sum(price) AS row FROM table WHERE row.weight > 50; ``` After registering this custom syntax with Rhai, it can be used anywhere inside a script as @@ -84,9 +84,9 @@ For its evaluation, the callback function will receive the following list of inp * `inputs[0] = "sum"` - math operator * `inputs[1] = "price"` - field name -* `inputs[2] = Expression(table)` - data source -* `inputs[3] = "row"` - loop variable name +* `inputs[2] = "row"` - loop variable name +* `inputs[3] = Expression(table)` - data source * `inputs[4] = Expression(row.wright > 50)` - filter predicate -Other identifiers, such as `"calculate"`, `"from"`, as well as symbols such as `->` and `:`, +Other identifiers, such as `"calculate"`, `"FROM"`, as well as symbols such as `->` and `:` etc., are parsed in the order defined within the custom syntax. diff --git a/doc/src/language/dynamic.md b/doc/src/language/dynamic.md index 9a3c27b7..7909d162 100644 --- a/doc/src/language/dynamic.md +++ b/doc/src/language/dynamic.md @@ -6,8 +6,8 @@ Dynamic Values A `Dynamic` value can be _any_ type. However, under [`sync`], all types must be `Send + Sync`. -Use [`type_of()`] to Get Value Type ----------------------------------- +Use `type_of()` to Get Value Type +-------------------------------- Because [`type_of()`] a `Dynamic` value returns the type of the actual value, it is usually used to perform type-specific actions based on the actual value's type. diff --git a/doc/src/language/eval.md b/doc/src/language/eval.md index 0af40142..fec9569a 100644 --- a/doc/src/language/eval.md +++ b/doc/src/language/eval.md @@ -65,17 +65,15 @@ disable `eval` by overloading it, probably with something that throws. ```rust fn eval(script) { throw "eval is evil! I refuse to run " + script } -let x = eval("40 + 2"); // 'eval' here throws "eval is evil! I refuse to run 40 + 2" +let x = eval("40 + 2"); // throws "eval is evil! I refuse to run 40 + 2" ``` Or overload it from Rust: ```rust -fn alt_eval(script: String) -> Result<(), Box> { +engine.register_result_fn("eval", |script: String| -> Result<(), Box> { Err(format!("eval is evil! I refuse to run {}", script).into()) -} - -engine.register_result_fn("eval", alt_eval); +}); ``` diff --git a/doc/src/language/for.md b/doc/src/language/for.md index 15ea22e2..493d8759 100644 --- a/doc/src/language/for.md +++ b/doc/src/language/for.md @@ -9,6 +9,9 @@ is provided by the `for` ... `in` loop. Like C, `continue` can be used to skip to the next iteration, by-passing all following statements; `break` can be used to break out of the loop unconditionally. +To loop through a number sequence (with or without steps), use the `range` function to +return a numeric iterator. + ```rust // Iterate through string, yielding characters let s = "hello, world!"; diff --git a/doc/src/language/modules/export.md b/doc/src/language/modules/export.md index 2152dc74..546b5952 100644 --- a/doc/src/language/modules/export.md +++ b/doc/src/language/modules/export.md @@ -20,14 +20,15 @@ Global Variables The `export` statement, which can only be at global level, exposes selected variables as members of a module. -Variables not exported are _private_ and hidden to the outside. +Variables not exported are _private_ and hidden. They are merely used to initialize the module, +but cannot be accessed from outside. Everything exported from a module is **constant** (**read-only**). ```rust // This is a module script. -let private = 123; // variable not exported - default hidden +let hidden = 123; // variable not exported - default hidden let x = 42; // this will be exported below export x; // the variable 'x' is exported under its own name @@ -43,9 +44,6 @@ export x as answer; // the variable 'x' is exported under the alias 'answer' } ``` -[`private`] variables are used to initialize the module. -They cannot be used apart from this. - Functions --------- diff --git a/doc/src/language/num-op.md b/doc/src/language/num-op.md index fa96f1e9..f1db4d46 100644 --- a/doc/src/language/num-op.md +++ b/doc/src/language/num-op.md @@ -10,7 +10,7 @@ Unary Operators | Operator | Description | | -------- | ----------- | -| `+` | Plus | +| `+` | Positive | | `-` | Negative | ```rust diff --git a/doc/src/language/timestamps.md b/doc/src/language/timestamps.md index 47ba302b..8fef7cdd 100644 --- a/doc/src/language/timestamps.md +++ b/doc/src/language/timestamps.md @@ -1,5 +1,5 @@ -`timestamp`'s -============= +`timestamp` +=========== {{#include ../links.md}} diff --git a/doc/src/language/type-of.md b/doc/src/language/type-of.md index a2869539..02ddbc28 100644 --- a/doc/src/language/type-of.md +++ b/doc/src/language/type-of.md @@ -1,5 +1,5 @@ -`type_of` -========= +`type_of()` +=========== {{#include ../links.md}} diff --git a/doc/src/patterns/config.md b/doc/src/patterns/config.md index e2d0db54..c87a0fb3 100644 --- a/doc/src/patterns/config.md +++ b/doc/src/patterns/config.md @@ -137,7 +137,7 @@ For example, the above configuration example may be expressed by this custom syn id "hello"; // Add to list -list +"foo" +list + "foo"; // Add to map map "bar" => true; diff --git a/doc/src/patterns/control.md b/doc/src/patterns/control.md index af6021e6..52b6b23c 100644 --- a/doc/src/patterns/control.md +++ b/doc/src/patterns/control.md @@ -68,19 +68,19 @@ let bunny: Rc> = Rc::new(RefCell::(EnergizerBunny::new() // Notice 'move' is used to move the shared API object into the closure. let b = bunny.clone(); engine.register_fn("bunny_power", move |on: bool| { - if on { - if b.borrow().is_going() { - println!("Still going..."); - } else { - b.borrow_mut().go(); - } + if on { + if b.borrow().is_going() { + println!("Still going..."); } else { - if b.borrow().is_going() { - b.borrow_mut().stop(); - } else { - println!("Already out of battery!"); - } + b.borrow_mut().go(); } + } else { + if b.borrow().is_going() { + b.borrow_mut().stop(); + } else { + println!("Already out of battery!"); + } + } }); let b = bunny.clone(); @@ -88,24 +88,24 @@ engine.register_fn("bunny_is_going", move || b.borrow().is_going()); let b = bunny.clone(); engine.register_fn("bunny_get_speed", move || - if b.borrow().is_going() { b.borrow().get_speed() } else { 0 } + if b.borrow().is_going() { b.borrow().get_speed() } else { 0 } ); let b = bunny.clone(); engine.register_result_fn("bunny_set_speed", move |speed: i64| - if speed <= 0 { - return Err("Speed must be positive!".into()); - } else if speed > 100 { - return Err("Bunny will be going too fast!".into()); - } + if speed <= 0 { + return Err("Speed must be positive!".into()); + } else if speed > 100 { + return Err("Bunny will be going too fast!".into()); + } - if b.borrow().is_going() { - b.borrow_mut().set_speed(speed) - } else { - return Err("Bunny is not yet going!".into()); - } + if b.borrow().is_going() { + b.borrow_mut().set_speed(speed) + } else { + return Err("Bunny is not yet going!".into()); + } - Ok(().into()) + Ok(().into()) ); ``` diff --git a/doc/src/patterns/oop.md b/doc/src/patterns/oop.md index 823df31e..b0e454c6 100644 --- a/doc/src/patterns/oop.md +++ b/doc/src/patterns/oop.md @@ -6,8 +6,8 @@ Object-Oriented Programming (OOP) Rhai does not have _objects_ per se, but it is possible to _simulate_ object-oriented programming. -Use [Object Maps] to Simulate OOP --------------------------------- +Use Object Maps to Simulate OOP +------------------------------ Rhai's [object maps] has [special support for OOP]({{rootUrl}}/language/object-maps-oop.md). @@ -22,8 +22,8 @@ a valid [function pointer] (perhaps defined via an [anonymous function] or more then the call will be dispatched to the actual function with `this` binding to the [object map] itself. -Use Anonymous Functions to Define Methods ----------------------------------------- +Use Closures to Define Methods +----------------------------- [Anonymous functions] or [closures] defined as values for [object map] properties take on a syntactic shape that resembles very closely that of class methods in an OOP language. diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index 24107fff..65bb0014 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -5,9 +5,11 @@ Export a Rust Module to Rhai 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. This code -is exactly what would need to be written by hand to achieve the same goal, and is custom fit -to each exported item. +code and metadata to allow Rhai access to its public (i.e. marked `pub`) functions, constants +and sub-modules. + +This code is exactly what would need to be written by hand to achieve the same goal, +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. @@ -16,15 +18,19 @@ registered as a [custom package]. This is done by using the `exported_module!` m `#[export_module]` and `exported_module!` ---------------------------------------- -Apply `#[export_module]` onto a Rust module to convert all `pub` functions into Rhai plugin -functions. +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 #[export_module] mod my_module { - // This constant will be registered as a the constant variable 'SOME_NUMBER'. + // This constant will be registered as the constant variable 'SOME_NUMBER'. + // Ignored when loaded as a package. pub const SOME_NUMBER: i64 = 42; // This function will be registered as 'greet'. @@ -39,14 +45,23 @@ mod my_module { pub fn increment(num: &mut i64) { *num += 1; } - // This function is NOT registered. + // This function is not 'pub', so NOT registered. fn mystic_number() -> i64 { 42 } + + // This sub-module is ignored when loaded as a package. + pub mod my_sub_module { + // This function is ignored when loaded as a package. + // Otherwise it is a valid registered function under a sub-module. + pub fn get_info() -> String { + "hello".to_string() + } + } } ``` -In order to load this into an [`Engine`], use the `load_package` method on the exported module: +The simplest way to load this into an [`Engine`] is to use the `load_package` method on the exported module: ```rust fn main() { @@ -55,7 +70,7 @@ fn main() { // The macro call creates the Rhai module. let module = exported_module!(my_module); - // A module can simply be loaded, registering all public its contents. + // A module can simply be loaded, registering all public functions. engine.load_package(module); } ``` @@ -77,9 +92,12 @@ increment(x); x == 43; ``` -Registering this as a custom package is almost the same, except that a module resolver must -point to the module, rather than being loaded directly. See the [module] section for more -information. +Notice that, when using a [module] as a [package], only functions registered at the _top level_ +can be accessed. Variables as well as sub-modules are ignored. + +Using this directly as a Rhai module is almost the same, except that a [module resolver] must +be used to serve the module, and the module is loaded via `import` statements. +See the [module] section for more information. Function Overloading and Operators @@ -91,7 +109,8 @@ attribute to individual functions. The text string given as the `name` parameter to `#[rhai_fn]` is used to register the function with the [`Engine`], disregarding the actual name of the function. -With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the same name in Rhai, so long as they have different parameters. +With `#[rhai_fn(name = "...")]`, multiple functions may be registered under the same name in Rhai, +so long as they have different parameters. Operators (which require function names that are not valid for Rust) can also be registered this way. diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 26441067..d2f68e54 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -7,7 +7,8 @@ Create a Module from Rust Create via Plugin ----------------- -By far the simplest way to create a [module] is via a [plugin module]. +By far the simplest way to create a [module] is via a [plugin module] +which converts a normal Rust module into a Rhai [module] via procedural macros. Create via `Module` API @@ -21,7 +22,8 @@ For the complete `Module` API, refer to the [documentation](https://docs.rs/rhai Make the `Module` Available to the `Engine` ------------------------------------------ -In order to _use_ a custom module, there must be a [module resolver]. +In order to _use_ a custom module, there must be a [module resolver], which serves the module when +loaded via `import` statements. The easiest way is to use, for example, the [`StaticModuleResolver`][module resolver] to hold such a custom module. diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 53ac17a0..01d11b12 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -58,16 +58,16 @@ 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 `Module::merge_flatten` from within -`rhai::def_package!` which simply merges in all the functions defined within a [plugin module]. +By far the easiest way to create a custom module is to call `rhai::plugins::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. -`rhai::plugins::combine_with_exported_module!` adds all functions and constants from the -[plugins][plugin module] definition into the package itself. +Because of the specific requirements of a [package], all sub-modules are _flattened_ +(i.e. all functions defined within sub-modules are pulled up and registered at the top level instead) +and so there will not be any sub-modules added to the package. -All sub-modules are _flattened_ (i.e. all functions and constants defined within sub-modules are registered -at the top level) and so there will not be any sub-modules added to the package. +Variables in the [plugin module] are ignored. ```rust // Import necessary types and traits. @@ -81,6 +81,8 @@ use rhai::plugin::*; // Define plugin module. #[export_module] mod my_module { + pub const MY_NUMBER: i64 = 42; + pub fn greet(name: &str) -> String { format!("hello, {}!", name) } @@ -107,16 +109,17 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { // Merge all registered functions and constants from the plugin module into the custom package. // - // Functions in the sub-module 'my_sub_module' are flattened and registered at the top level - // instead of in a sub-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 constant variable, 'MY_NUMBER', is ignored. + // // This call ends up registering three functions at the top level of the package: // 1) greet // 2) get_num - // 3) get_sub_num (flattened from sub-module 'my_sub_module') + // 3) get_sub_num (pulled up from 'my_sub_module') // combine_with_exported_module!(module, "my-functions", my_module)); }); diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index a5df94cd..f2e6b598 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -37,7 +37,7 @@ namespace alias specified in an [`import`] statement (see also [modules]). A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with the `import` statement). -Functions in a package are _flattened_, meaning that functions from sub-modules must be pulled up to the root level. +Sub-modules in a package are _flattened_, meaning that functions from them must be pulled up to the root level. In other words, there can be no sub-modules in a package, and everything should reside in one, flat namespace. Only functions matter for a package. Constant variables registered in a package are ignored. diff --git a/doc/src/start/builds/performance.md b/doc/src/start/builds/performance.md index a0c65fe5..78ca762e 100644 --- a/doc/src/start/builds/performance.md +++ b/doc/src/start/builds/performance.md @@ -13,23 +13,26 @@ If only a single integer type is needed in scripts - most of the time this is th lots of functions related to other integer types that will never be used. As a result, [`Engine`] creation will be faster because fewer functions need to be loaded. +The [`only_i32`] and [`only_i64`] features disable all integer types except `i32` or `i64` respectively. + Use Only 32-Bit Numbers ---------------------- -If only 32-bit integers are needed - again, most of the time this is the case - using [`only_i32`] disables also `i64`. +If only 32-bit integers are needed - again, most of the time this is the case - turn on [`only_i32`]. +Under this feature, only `i32` is supported as a built-in integer type and no others. -On 64-bit targets this may not gain much, but on some 32-bit targets this improves performance due to 64-bit arithmetic -requiring more CPU cycles to complete. +On 64-bit targets this may not gain much, but on certain 32-bit targets this improves performance +due to 64-bit arithmetic requiring more CPU cycles to complete. Minimize Size of `Dynamic` ------------------------- -Turning on [`no_float`], and [`only_i32`] makes the key [`Dynamic`] data type only 8 bytes small on 32-bit targets -while normally it can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`. +Turning on [`no_float`] and [`only_i32`] on 32-bit targets makes the critical [`Dynamic`] data type only 8 bytes long. +Normally [`Dynamic`] can be up to 16 bytes (e.g. on x86/x64 CPU's) in order to hold an `i64` or `f64`. -Making [`Dynamic`] small helps performance due to better cache efficiency. +A small [`Dynamic`] helps performance due to better cache efficiency. Use `ImmutableString` @@ -41,17 +44,17 @@ cloning when passing function arguments. Rhai's internal string type is `ImmutableString` (basically `Rc` or `Arc` depending on the [`sync`] feature). It is cheap to clone, but expensive to modify (a new copy of the string must be made in order to change it). -Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (which maps to `ImmutableString`) +Therefore, functions taking `String` parameters should use `ImmutableString` or `&str` (both map to `ImmutableString`) for the best performance with Rhai. Disable Closures ---------------- -Support for [closures], including capturing shared values, adds material overhead to script evaluation. +Support for [closures] that capture shared variables adds material overhead to script evaluation. -This is because every data access must be checked whether it is a shared value, and if so, take a read -or write lock before reading it. +This is because every data access must be checked whether it is a shared value and, if so, take a read +lock before reading it. -Use [`no_closure`] to disable closure and capturing support and optimize the hot path +Use [`no_closure`] to disable closure and capturing support to optimize the hot path because there is no need to take locks for shared data. diff --git a/doc/src/start/features.md b/doc/src/start/features.md index 3e8067fa..9cd86f52 100644 --- a/doc/src/start/features.md +++ b/doc/src/start/features.md @@ -11,23 +11,23 @@ Notice that this deviates from Rust norm where features are _additive_. Excluding unneeded functionalities can result in smaller, faster builds as well as more control over what a script can (or cannot) do. -| Feature | Description | -| ------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | -| `unchecked` | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit.
Beware that a bad script may panic the entire system! | -| `sync` | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and [`AST`], are all `Send + Sync`. | -| `no_optimize` | Disable [script optimization]. | -| `no_float` | Disable floating-point numbers and math. | -| `only_i32` | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. | -| `only_i64` | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. | -| `no_index` | Disable [arrays] and indexing features. | -| `no_object` | Disable support for [custom types] and [object maps]. | -| `no_function` | Disable script-defined [functions]. | -| `no_module` | Disable loading external [modules]. | -| `no_closure` | Disable [capturing][automatic currying] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. | -| `no_std` | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. | -| `serde` | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. | -| `internals` | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. | -| `unicode-xid-ident` | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. | +| Feature | Additive? | Description | +| ------------------- | :-------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `unchecked` | No | Disable arithmetic checking (such as over-flows and division by zero), call stack depth limit, operations count limit and modules loading limit.
Beware that a bad script may panic the entire system! | +| `sync` | No | Restrict all values types to those that are `Send + Sync`. Under this feature, all Rhai types, including [`Engine`], [`Scope`] and [`AST`], are all `Send + Sync`. | +| `no_optimize` | No | Disable [script optimization]. | +| `no_float` | No | Disable floating-point numbers and math. | +| `only_i32` | No | Set the system integer type to `i32` and disable all other integer types. `INT` is set to `i32`. | +| `only_i64` | No | Set the system integer type to `i64` and disable all other integer types. `INT` is set to `i64`. | +| `no_index` | No | Disable [arrays] and indexing features. | +| `no_object` | No | Disable support for [custom types] and [object maps]. | +| `no_function` | No | Disable script-defined [functions]. | +| `no_module` | No | Disable loading external [modules]. | +| `no_closure` | No | Disable [capturing][automatic currying] external variables in [anonymous functions] to simulate _closures_, or [capturing the calling scope]({{rootUrl}}/language/fn-capture.md) in function calls. | +| `no_std` | No | Build for `no-std` (implies `no_closure`). Notice that additional dependencies will be pulled in to replace `std` features. | +| `serde` | Yes | Enable serialization/deserialization via `serde`. Notice that the [`serde`](https://crates.io/crates/serde) crate will be pulled in together with its dependencies. | +| `internals` | Yes | Expose internal data structures (e.g. [`AST`] nodes). Beware that Rhai internals are volatile and may change from version to version. | +| `unicode-xid-ident` | No | Allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) as identifiers. | Example @@ -57,7 +57,7 @@ This configuration is perfect for an expression parser in a 32-bit embedded syst Caveat - Features Are Not Additive --------------------------------- -Rhai features are not strictly _additive_ - i.e. they do not only add optional functionalities. +Most Rhai features are not strictly _additive_ - i.e. they do not only add optional functionalities. In fact, most features are _subtractive_ - i.e. they _remove_ functionalities. From 39546b70533d09f6158a4a6222e84127701cecbc Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 19 Sep 2020 18:12:23 +0800 Subject: [PATCH 23/34] Enable &str and String parameters in module functions. --- src/module.rs | 56 +++++++++++++++++++++++++++++------------------- tests/modules.rs | 46 +++++++++++++++++++++++++++++++++++---- 2 files changed, 76 insertions(+), 26 deletions(-) diff --git a/src/module.rs b/src/module.rs index 4d70b169..51d5539c 100644 --- a/src/module.rs +++ b/src/module.rs @@ -4,10 +4,11 @@ use crate::any::{Dynamic, Variant}; use crate::calc_fn_hash; use crate::engine::Engine; use crate::fn_native::{CallableFunction as Func, FnCallArgs, IteratorFn, SendSync}; +use crate::fn_register::by_value as cast_arg; use crate::parser::{FnAccess, FnAccess::Public, ScriptFnDef}; use crate::result::EvalAltResult; use crate::token::{Position, Token}; -use crate::utils::{StaticVec, StraightHasherBuilder}; +use crate::utils::{ImmutableString, StaticVec, StraightHasherBuilder}; #[cfg(not(feature = "no_function"))] use crate::fn_native::Shared; @@ -32,7 +33,6 @@ use crate::stdlib::{ collections::HashMap, fmt, format, iter::empty, - mem, num::NonZeroUsize, ops::{Deref, DerefMut}, string::{String, ToString}, @@ -396,9 +396,21 @@ impl Module { arg_types.len() }; - let hash_fn = calc_fn_hash(empty(), &name, args_len, arg_types.iter().cloned()); + let params = arg_types + .into_iter() + .cloned() + .map(|id| { + if id == TypeId::of::<&str>() { + TypeId::of::() + } else if id == TypeId::of::() { + TypeId::of::() + } else { + id + } + }) + .collect(); - let params = arg_types.into_iter().cloned().collect(); + let hash_fn = calc_fn_hash(empty(), &name, args_len, arg_types.iter().cloned()); self.functions .insert(hash_fn, (name, access, params, func.into())); @@ -518,7 +530,7 @@ impl Module { func: impl Fn(A) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - func(mem::take(args[0]).cast::()).map(Dynamic::from) + func(cast_arg::(&mut args[0])).map(Dynamic::from) }; let arg_types = [TypeId::of::()]; self.set_fn(name, Public, &arg_types, Func::from_pure(Box::new(f))) @@ -592,8 +604,8 @@ impl Module { func: impl Fn(A, B) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let a = mem::take(args[0]).cast::(); - let b = mem::take(args[1]).cast::(); + let a = cast_arg::(&mut args[0]); + let b = cast_arg::(&mut args[1]); func(a, b).map(Dynamic::from) }; @@ -623,7 +635,7 @@ impl Module { func: impl Fn(&mut A, B) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let b = mem::take(args[1]).cast::(); + let b = cast_arg::(&mut args[1]); let a = &mut args[0].write_lock::().unwrap(); func(a, b).map(Dynamic::from) @@ -709,9 +721,9 @@ impl Module { func: impl Fn(A, B, C) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let a = mem::take(args[0]).cast::(); - let b = mem::take(args[1]).cast::(); - let c = mem::take(args[2]).cast::(); + let a = cast_arg::(&mut args[0]); + let b = cast_arg::(&mut args[1]); + let c = cast_arg::(&mut args[2]); func(a, b, c).map(Dynamic::from) }; @@ -746,8 +758,8 @@ impl Module { func: impl Fn(&mut A, B, C) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let b = mem::take(args[1]).cast::(); - let c = mem::take(args[2]).cast::(); + let b = cast_arg::(&mut args[2]); + let c = cast_arg::(&mut args[3]); let a = &mut args[0].write_lock::().unwrap(); func(a, b, c).map(Dynamic::from) @@ -780,8 +792,8 @@ impl Module { func: impl Fn(&mut A, B, C) -> FuncReturn<()> + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let b = mem::take(args[1]).cast::(); - let c = mem::take(args[2]).cast::(); + let b = cast_arg::(&mut args[1]); + let c = cast_arg::(&mut args[2]); let a = &mut args[0].write_lock::().unwrap(); func(a, b, c).map(Dynamic::from) @@ -858,10 +870,10 @@ impl Module { func: impl Fn(A, B, C, D) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let a = mem::take(args[0]).cast::(); - let b = mem::take(args[1]).cast::(); - let c = mem::take(args[2]).cast::(); - let d = mem::take(args[3]).cast::(); + let a = cast_arg::(&mut args[0]); + let b = cast_arg::(&mut args[1]); + let c = cast_arg::(&mut args[2]); + let d = cast_arg::(&mut args[3]); func(a, b, c, d).map(Dynamic::from) }; @@ -902,9 +914,9 @@ impl Module { func: impl Fn(&mut A, B, C, D) -> FuncReturn + SendSync + 'static, ) -> u64 { let f = move |_: &Engine, _: &Module, args: &mut FnCallArgs| { - let b = mem::take(args[1]).cast::(); - let c = mem::take(args[2]).cast::(); - let d = mem::take(args[3]).cast::(); + let b = cast_arg::(&mut args[1]); + let c = cast_arg::(&mut args[2]); + let d = cast_arg::(&mut args[3]); let a = &mut args[0].write_lock::().unwrap(); func(a, b, c, d).map(Dynamic::from) diff --git a/tests/modules.rs b/tests/modules.rs index 0744fd8c..e3475584 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -1,7 +1,7 @@ #![cfg(not(feature = "no_module"))] use rhai::{ - module_resolvers::StaticModuleResolver, Dynamic, Engine, EvalAltResult, Module, ParseError, - ParseErrorType, Scope, INT, + module_resolvers::StaticModuleResolver, Dynamic, Engine, EvalAltResult, ImmutableString, + Module, ParseError, ParseErrorType, Scope, INT, }; #[test] @@ -79,12 +79,12 @@ fn test_module_resolver() -> Result<(), Box> { }); #[cfg(not(feature = "no_float"))] - module.set_fn_4_mut( + module.set_fn_4_mut( "sum_of_three_args".to_string(), |target: &mut INT, a: INT, b: INT, c: f64| { *target = a + b + c as INT; Ok(()) - } + }, ); resolver.insert("hello", module); @@ -316,3 +316,41 @@ fn test_module_export() -> Result<(), Box> { Ok(()) } + +#[test] +fn test_module_str() -> Result<(), Box> { + fn test_fn(_input: ImmutableString) -> Result> { + Ok(42) + } + fn test_fn2(_input: &str) -> Result> { + Ok(42) + } + fn test_fn3(_input: String) -> Result> { + Ok(42) + } + + let mut engine = rhai::Engine::new(); + let mut module = Module::new(); + module.set_fn_1("test", test_fn); + module.set_fn_1("test2", test_fn2); + module.set_fn_1("test3", test_fn3); + + let mut static_modules = rhai::module_resolvers::StaticModuleResolver::new(); + static_modules.insert("test", module); + engine.set_module_resolver(Some(static_modules)); + + assert_eq!( + engine.eval::(r#"import "test" as test; test::test("test");"#)?, + 42 + ); + assert_eq!( + engine.eval::(r#"import "test" as test; test::test2("test");"#)?, + 42 + ); + assert_eq!( + engine.eval::(r#"import "test" as test; test::test3("test");"#)?, + 42 + ); + + Ok(()) +} From 20a453b3cc480b8cbeae7ff5f51c2dd0f163c397 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 19 Sep 2020 18:18:40 +0800 Subject: [PATCH 24/34] Enable String parameters. --- codegen/src/function.rs | 23 ++++++++--- codegen/src/rhai_module.rs | 6 +++ codegen/src/test/function.rs | 12 ++---- codegen/src/test/module.rs | 76 ++++++++++++++++++++++++++++++++---- tests/plugins.rs | 5 +++ 5 files changed, 100 insertions(+), 22 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index aa059013..82e76214 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -638,11 +638,13 @@ impl ExportedFn { // Handle the rest of the arguments, which all are passed by value. // // The only exception is strings, which need to be downcast to ImmutableString to enable a - // zero-copy conversion to &str by reference. + // zero-copy conversion to &str by reference, or a cloned String. let str_type_path = syn::parse2::(quote! { str }).unwrap(); + let string_type_path = syn::parse2::(quote! { String }).unwrap(); for (i, arg) in self.arg_list().enumerate().skip(skip_first_arg as usize) { let var = syn::Ident::new(&format!("arg{}", i), proc_macro2::Span::call_site()); - let is_str_ref; + let is_string; + let is_ref; match arg { syn::FnArg::Typed(pattern) => { let arg_type: &syn::Type = pattern.ty.as_ref(); @@ -653,15 +655,24 @@ impl ExportedFn { .. }) => match elem.as_ref() { &syn::Type::Path(ref p) if p.path == str_type_path => { - is_str_ref = true; + is_string = true; + is_ref = true; quote_spanned!(arg_type.span()=> mem::take(args[#i]) .clone().cast::()) } _ => panic!("internal error: why wasn't this found earlier!?"), }, + &syn::Type::Path(ref p) if p.path == string_type_path => { + is_string = true; + is_ref = false; + quote_spanned!(arg_type.span()=> + mem::take(args[#i]) + .clone().cast::<#arg_type>()) + } _ => { - is_str_ref = false; + is_string = false; + is_ref = false; quote_spanned!(arg_type.span()=> mem::take(args[#i]).clone().cast::<#arg_type>()) } @@ -673,7 +684,7 @@ impl ExportedFn { }) .unwrap(), ); - if !is_str_ref { + if !is_string { input_type_exprs.push( syn::parse2::(quote_spanned!( arg_type.span()=> TypeId::of::<#arg_type>() @@ -691,7 +702,7 @@ impl ExportedFn { } syn::FnArg::Receiver(_) => panic!("internal error: how did this happen!?"), } - if !is_str_ref { + if !is_ref { unpack_exprs.push(syn::parse2::(quote! { #var }).unwrap()); } else { unpack_exprs.push(syn::parse2::(quote! { &#var }).unwrap()); diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 1859ba42..c25c9484 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -19,6 +19,7 @@ pub(crate) fn generate_body( let mut add_mod_blocks: Vec = Vec::new(); let mut set_flattened_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); + let string_type_path = syn::parse2::(quote! { String }).unwrap(); for (const_name, _, _) in consts { let const_literal = syn::LitStr::new(&const_name, proc_macro2::Span::call_site()); @@ -97,6 +98,11 @@ pub(crate) fn generate_body( } _ => panic!("internal error: non-string shared reference!?"), }, + syn::Type::Path(ref p) if p.path == string_type_path => { + syn::parse2::(quote! { + ImmutableString }) + .unwrap() + } syn::Type::Reference(syn::TypeReference { mutability: Some(_), ref elem, diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 61c1f942..b6d8e894 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -245,8 +245,7 @@ mod generate_tests { false } }); - /* - let (actual_diff, expected_diff) = { + let (_actual_diff, _expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); for (a, e) in _iter.take(50) { @@ -255,13 +254,10 @@ mod generate_tests { } (actual_diff, expected_diff) }; - */ eprintln!("actual != expected, diverge at char {}", counter); - /* - eprintln!(" actual: {}", actual_diff); - eprintln!("expected: {}", expected_diff); - assert!(false); - */ + // eprintln!(" actual: {}", _actual_diff); + // eprintln!("expected: {}", _expected_diff); + // assert!(false); } assert_eq!(actual, expected); } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 59e58ce7..a2ab7eab 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -226,8 +226,7 @@ mod generate_tests { false } }); - /* - let (actual_diff, expected_diff) = { + let (_actual_diff, _expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); for (a, e) in _iter.take(50) { @@ -236,13 +235,10 @@ mod generate_tests { } (actual_diff, expected_diff) }; - */ eprintln!("actual != expected, diverge at char {}", counter); - /* - eprintln!(" actual: {}", actual_diff); - eprintln!("expected: {}", expected_diff); - assert!(false); - */ + // eprintln!(" actual: {}", _actual_diff); + // eprintln!("expected: {}", _expected_diff); + // assert!(false); } assert_eq!(actual, expected); } @@ -978,6 +974,70 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_string_arg_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod str_fn { + pub fn print_out_to(x: String) { + x + 1 + } + } + }; + + let expected_tokens = quote! { + pub mod str_fn { + pub fn print_out_to(x: String) { + 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("print_out_to", FnAccess::Public, + &[core::any::TypeId::of::()], + CallableFunction::from_plugin(print_out_to_token())); + if flatten {} else {} + } + #[allow(non_camel_case_types)] + struct print_out_to_token(); + impl PluginFunction for print_out_to_token { + fn call(&self, + args: &mut [&mut Dynamic] + ) -> Result> { + debug_assert_eq!(args.len(), 1usize, + "wrong arg count: {} != {}", args.len(), 1usize); + let arg0 = mem::take(args[0usize]).clone().cast::(); + Ok(Dynamic::from(print_out_to(arg0))) + } + + fn is_method_call(&self) -> bool { false } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(print_out_to_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::()].into_boxed_slice() + } + } + pub fn print_out_to_token_callable() -> CallableFunction { + CallableFunction::from_plugin(print_out_to_token()) + } + pub fn print_out_to_token_input_types() -> Box<[TypeId]> { + print_out_to_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_mut_ref_fn_module() { let input_tokens: TokenStream = quote! { diff --git a/tests/plugins.rs b/tests/plugins.rs index 2bee84e4..ce403dd6 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -24,6 +24,10 @@ mod test { } } + pub fn hash(_text: String) -> INT { + 42 + } + #[rhai_fn(name = "test", name = "hi")] #[inline(always)] pub fn len(array: &mut Array, mul: INT) -> INT { @@ -77,6 +81,7 @@ fn test_plugins_package() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval::("let a = [1, 2, 3]; a.foo")?, 1); + assert_eq!(engine.eval::(r#"hash("hello")"#)?, 42); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; hi(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); From 3341f40fd2f2ed23cbc1efb677ecd87004534154 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Sep 2020 10:50:58 +0800 Subject: [PATCH 25/34] Refine docs. --- RELEASES.md | 2 ++ doc/src/language/arrays.md | 15 ++++++++----- doc/src/rust/custom.md | 40 +++++++++++++++++++++++++++++++-- doc/src/rust/disable-custom.md | 8 +++---- doc/src/rust/getters-setters.md | 14 ++++++++++++ doc/src/rust/indexers.md | 12 ++++++++++ src/api.rs | 5 ----- 7 files changed, 79 insertions(+), 17 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index cb155381..9d8476fb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -9,6 +9,8 @@ Bug fixes * `if` statement with an empty `true` block would not evaluate the `false` block. This is now fixed. * Fixes a bug in `Module::set_fn_4_mut`. +* Module API's now properly handle `&str` and `String` parameters. +* Indexers are available under `no_object`. New features ------------ diff --git a/doc/src/language/arrays.md b/doc/src/language/arrays.md index 69f24103..5c0ce9e9 100644 --- a/doc/src/language/arrays.md +++ b/doc/src/language/arrays.md @@ -45,6 +45,15 @@ The following methods (mostly defined in the [`BasicArrayPackage`][packages] but | `truncate` | target length | cuts off the array at exactly a specified length (discarding all subsequent elements) | +Use Custom Types With Arrays +--------------------------- + +To use a [custom type] with arrays, a number of array functions need to be manually implemented, +in particular `push`, `pad` and the `==` operator (in order to support the `in` operator). + +See the section on [custom types] for more details. + + Examples -------- @@ -123,9 +132,3 @@ y.clear(); // empty the array y.len == 0; ``` - -`push` and `pad` are only defined for standard built-in types. For custom types, type-specific versions must be registered: - -```rust -engine.register_fn("push", |list: &mut Array, item: MyType| list.push(Box::new(item)) ); -``` diff --git a/doc/src/rust/custom.md b/doc/src/rust/custom.md index b2325204..22bea701 100644 --- a/doc/src/rust/custom.md +++ b/doc/src/rust/custom.md @@ -38,12 +38,14 @@ let result = engine.eval::("let x = new_ts(); x.update(); x")?; println!("result: {}", result.field); // prints 42 ``` + Register a Custom Type --------------------- A custom type must implement `Clone` as this allows the [`Engine`] to pass by value. -Notice that the custom type needs to be _registered_ using `Engine::register_type`. +Notice that the custom type needs to be _registered_ using `Engine::register_type` +or `Engine::register_type_with_name`. ```rust #[derive(Clone)] @@ -66,7 +68,8 @@ let mut engine = Engine::new(); engine.register_type::(); ``` -Methods on The Custom Type + +Methods on the Custom Type ------------------------- To use native custom types, methods and functions in Rhai scripts, simply register them @@ -85,6 +88,7 @@ so that invoking methods can update the types. All other parameters in Rhai are **IMPORTANT: Rhai does NOT support normal references (i.e. `&T`) as parameters.** + Use the Custom Type in Scripts ----------------------------- @@ -97,6 +101,7 @@ let result = engine.eval::("let x = new_ts(); x.update(); x")?; println!("result: {}", result.field); // prints 42 ``` + Method-Call Style vs. Function-Call Style ---------------------------------------- @@ -127,6 +132,7 @@ Under [`no_object`], however, the _method_ style of function calls let result = engine.eval::<()>("let x = [1, 2, 3]; x.clear()")?; ``` + `type_of()` a Custom Type ------------------------- @@ -150,3 +156,33 @@ engine let x = new_ts(); x.type_of() == "Hello"; ``` + + +Use the Custom Type With Arrays +------------------------------ + +The `push` and `pad` functions for [arrays] are only defined for standard built-in types. +For custom types, type-specific versions must be registered: + +```rust +engine + .register_fn("push", |list: &mut Array, item: TestStruct| { + list.push(Dynamic::from(item)); + }).register_fn("pad", |list: &mut Array, len: i64, item: TestStruct| { + if len as usize > list.len() { + list.resize(len as usize, item); + } + }); +``` + +In particular, in order to use the `in` operator with a custom type for an [array], +the `==` operator must be registered for that custom type: + +```rust +// Assume 'TestStruct' implements `PartialEq` +engine.register_fn("==", |item1: &mut TestStruct, item2: TestStruct| item1 == item2); + +// Then this works in Rhai: +let item = new_ts(); // construct a new 'TestStruct' +item in array; // 'in' operator uses '==' +``` diff --git a/doc/src/rust/disable-custom.md b/doc/src/rust/disable-custom.md index a2c69c95..e5ad8a82 100644 --- a/doc/src/rust/disable-custom.md +++ b/doc/src/rust/disable-custom.md @@ -3,8 +3,8 @@ Disable Custom Types {{#include ../links.md}} -The custom types API `register_type`, `register_type_with_name`, `register_get`, `register_set`, `register_get_set`, -`register_indexer_get`, `register_indexer_set` and `register_indexer_get_set` are not available under [`no_object`]. +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`]. -The indexers API `register_indexer_get`, `register_indexer_set` and `register_indexer_get_set` are also -not available under [`no_index`]. +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`]. diff --git a/doc/src/rust/getters-setters.md b/doc/src/rust/getters-setters.md index 7597e8d5..6f6184ce 100644 --- a/doc/src/rust/getters-setters.md +++ b/doc/src/rust/getters-setters.md @@ -7,6 +7,20 @@ A custom type can also expose members by registering `get` and/or `set` function Getters and setters each take a `&mut` reference to the first parameter. +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_set` | Register a setter | _Any_ | +| `register_get_set` | Short-hand to register both a getter and a setter | _None_ | +| `register_get_result` | Register a getter | `Result>` | +| `register_set_result` | Register a setter | `Result>` | + + +Examples +-------- + ```rust #[derive(Clone)] struct TestStruct { diff --git a/doc/src/rust/indexers.md b/doc/src/rust/indexers.md index 94a6b5f1..69f73965 100644 --- a/doc/src/rust/indexers.md +++ b/doc/src/rust/indexers.md @@ -16,6 +16,18 @@ Indexers are disabled when the [`no_index`] feature is used. For efficiency reasons, indexers **cannot** be used to overload (i.e. override) built-in indexing operations for [arrays] and [object maps]. +| `Engine` API | Description | Return Value of Function | +| ----------------------------- | -------------------------------------------------------- | :-----------------------------------: | +| `register_indexer_get` | Register an index getter | _Any_ | +| `register_indexer_set` | Register an index setter | _Any_ | +| `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>` | +| `register_indexer_set_result` | Register an index setter | `Result>` | + + +Examples +-------- + ```rust #[derive(Clone)] struct TestStruct { diff --git a/src/api.rs b/src/api.rs index 899b1ab6..85e0aab3 100644 --- a/src/api.rs +++ b/src/api.rs @@ -463,7 +463,6 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] pub fn register_indexer_get( &mut self, @@ -516,7 +515,6 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] pub fn register_indexer_get_result( &mut self, @@ -564,7 +562,6 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] pub fn register_indexer_set( &mut self, @@ -617,7 +614,6 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] pub fn register_indexer_set_result( &mut self, @@ -664,7 +660,6 @@ impl Engine { /// # Ok(()) /// # } /// ``` - #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] pub fn register_indexer_get_set( &mut self, From 7c3498a4e18061a9dd3e2a1cc2dcfa6abef59f82 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 12:31:41 +0800 Subject: [PATCH 26/34] Prevent errors when using alternative error printout style. --- codegen/src/test/function.rs | 4 +++- codegen/src/test/module.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index a4a54959..31797438 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -245,7 +245,6 @@ mod generate_tests { false } }); - eprintln!("actual != expected, diverge at char {}", counter); /* let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); @@ -256,6 +255,9 @@ mod generate_tests { } (actual_diff, expected_diff) }; + */ + eprintln!("actual != expected, diverge at char {}", counter); + /* eprintln!(" actual: {}", actual_diff); eprintln!("expected: {}", expected_diff); assert!(false); diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index b560e94b..4d71f296 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -226,7 +226,6 @@ mod generate_tests { false } }); - eprintln!("actual != expected, diverge at char {}", counter); /* let (actual_diff, expected_diff) = { let mut actual_diff = String::new(); @@ -237,6 +236,9 @@ mod generate_tests { } (actual_diff, expected_diff) }; + */ + eprintln!("actual != expected, diverge at char {}", counter); + /* eprintln!(" actual: {}", actual_diff); eprintln!("expected: {}", expected_diff); assert!(false); From cfe28c92829ec1c17c61769333cf1098283002d6 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 13:59:38 +0800 Subject: [PATCH 27/34] Remove Position parameter from plugin call function. --- codegen/src/function.rs | 214 +++++++++++++++++++++-------------- codegen/src/test/function.rs | 12 +- codegen/src/test/module.rs | 38 +++---- src/fn_call.rs | 4 +- src/plugin.rs | 5 +- 5 files changed, 157 insertions(+), 116 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 75812dbd..aa059013 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -84,71 +84,91 @@ impl ExportedParams for ExportedFnParams { let mut skip = false; let mut special = FnSpecialAccess::None; for attr in attrs { - let crate::attrs::AttrItem { key, value, span: item_span } = attr; + let crate::attrs::AttrItem { + key, + value, + span: item_span, + } = attr; match (key.to_string().as_ref(), value) { ("get", None) | ("set", None) | ("name", None) => { return Err(syn::Error::new(key.span(), "requires value")) - }, + } ("name", Some(s)) if &s.value() == FN_IDX_GET => { - return Err(syn::Error::new(item_span, - "use attribute 'index_get' instead")) - }, + return Err(syn::Error::new( + item_span, + "use attribute 'index_get' instead", + )) + } ("name", Some(s)) if &s.value() == FN_IDX_SET => { - return Err(syn::Error::new(item_span, - "use attribute 'index_set' instead")) - }, + return Err(syn::Error::new( + item_span, + "use attribute 'index_set' instead", + )) + } ("name", Some(s)) if s.value().starts_with("get$") => { - return Err(syn::Error::new(item_span, - format!("use attribute 'getter = \"{}\"' instead", - &s.value()["get$".len()..]))) - }, + return Err(syn::Error::new( + item_span, + format!( + "use attribute 'getter = \"{}\"' instead", + &s.value()["get$".len()..] + ), + )) + } ("name", Some(s)) if s.value().starts_with("set$") => { - return Err(syn::Error::new(item_span, - format!("use attribute 'setter = \"{}\"' instead", - &s.value()["set$".len()..]))) - }, + return Err(syn::Error::new( + item_span, + format!( + "use attribute 'setter = \"{}\"' instead", + &s.value()["set$".len()..] + ), + )) + } ("name", Some(s)) if s.value().contains('$') => { - return Err(syn::Error::new(s.span(), - "Rhai function names may not contain dollar sign")) - }, + return Err(syn::Error::new( + s.span(), + "Rhai function names may not contain dollar sign", + )) + } ("name", Some(s)) if s.value().contains('.') => { - return Err(syn::Error::new(s.span(), - "Rhai function names may not contain dot")) - }, - ("name", Some(s)) => { - name.push(s.value()) - }, - ("set", Some(s)) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Property(Property::Set(syn::Ident::new(&s.value(), - s.span()))), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting setter")) + return Err(syn::Error::new( + s.span(), + "Rhai function names may not contain dot", + )) + } + ("name", Some(s)) => name.push(s.value()), + ("set", Some(s)) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Property(Property::Set( + syn::Ident::new(&s.value(), s.span()), + )), + _ => return Err(syn::Error::new(item_span.span(), "conflicting setter")), } - }, - ("get", Some(s)) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Property(Property::Get(syn::Ident::new(&s.value(), - s.span()))), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting getter")) + } + ("get", Some(s)) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Property(Property::Get( + syn::Ident::new(&s.value(), s.span()), + )), + _ => return Err(syn::Error::new(item_span.span(), "conflicting getter")), } - }, - ("index_get", None) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Index(Index::Get), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting index_get")) + } + ("index_get", None) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Index(Index::Get), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting index_get")) + } } - }, + } - ("index_set", None) => special = match special { - FnSpecialAccess::None => - FnSpecialAccess::Index(Index::Set), - _ => { - return Err(syn::Error::new(item_span.span(), "conflicting index_set")) + ("index_set", None) => { + special = match special { + FnSpecialAccess::None => FnSpecialAccess::Index(Index::Set), + _ => { + return Err(syn::Error::new(item_span.span(), "conflicting index_set")) + } } - }, + } ("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")) @@ -327,26 +347,40 @@ impl ExportedFn { } pub(crate) fn exported_names(&self) -> Vec { - let mut literals = self.params.name.as_ref() - .map(|v| v.iter() - .map(|s| syn::LitStr::new(s, proc_macro2::Span::call_site())).collect()) + let mut literals = self + .params + .name + .as_ref() + .map(|v| { + v.iter() + .map(|s| syn::LitStr::new(s, proc_macro2::Span::call_site())) + .collect() + }) .unwrap_or_else(|| Vec::new()); match self.params.special { - FnSpecialAccess::None => {}, - FnSpecialAccess::Property(Property::Get(ref g)) => - literals.push(syn::LitStr::new(&format!("get${}", g.to_string()), g.span())), - FnSpecialAccess::Property(Property::Set(ref s)) => - literals.push(syn::LitStr::new(&format!("set${}", s.to_string()), s.span())), - FnSpecialAccess::Index(Index::Get) => - literals.push(syn::LitStr::new(FN_IDX_GET, proc_macro2::Span::call_site())), - FnSpecialAccess::Index(Index::Set) => - literals.push(syn::LitStr::new(FN_IDX_SET, proc_macro2::Span::call_site())), + FnSpecialAccess::None => {} + FnSpecialAccess::Property(Property::Get(ref g)) => literals.push(syn::LitStr::new( + &format!("get${}", g.to_string()), + g.span(), + )), + FnSpecialAccess::Property(Property::Set(ref s)) => literals.push(syn::LitStr::new( + &format!("set${}", s.to_string()), + s.span(), + )), + FnSpecialAccess::Index(Index::Get) => { + literals.push(syn::LitStr::new(FN_IDX_GET, proc_macro2::Span::call_site())) + } + FnSpecialAccess::Index(Index::Set) => { + literals.push(syn::LitStr::new(FN_IDX_SET, proc_macro2::Span::call_site())) + } } if literals.is_empty() { - literals.push(syn::LitStr::new(&self.signature.ident.to_string(), - self.signature.ident.span())); + literals.push(syn::LitStr::new( + &self.signature.ident.to_string(), + self.signature.ident.span(), + )); } literals @@ -394,53 +428,61 @@ impl ExportedFn { match params.special { // 2a. Property getters must take only the subject as an argument. - FnSpecialAccess::Property(Property::Get(_)) if self.arg_count() != 1 => + FnSpecialAccess::Property(Property::Get(_)) if self.arg_count() != 1 => { return Err(syn::Error::new( self.signature.span(), "property getter requires exactly 1 argument", - )), + )) + } // 2b. Property getters must return a value. - FnSpecialAccess::Property(Property::Get(_)) if self.return_type().is_none() => + FnSpecialAccess::Property(Property::Get(_)) if self.return_type().is_none() => { return Err(syn::Error::new( self.signature.span(), - "property getter must return a value" - )), + "property getter must return a value", + )) + } // 3a. Property setters must take the subject and a new value as arguments. - FnSpecialAccess::Property(Property::Set(_)) if self.arg_count() != 2 => + FnSpecialAccess::Property(Property::Set(_)) if self.arg_count() != 2 => { return Err(syn::Error::new( self.signature.span(), "property setter requires exactly 2 arguments", - )), + )) + } // 3b. Property setters must return nothing. - FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => + FnSpecialAccess::Property(Property::Set(_)) if self.return_type().is_some() => { return Err(syn::Error::new( self.signature.span(), - "property setter must return no value" - )), + "property setter must return no value", + )) + } // 4a. Index getters must take the subject and the accessed "index" as arguments. - FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => + FnSpecialAccess::Index(Index::Get) if self.arg_count() != 2 => { return Err(syn::Error::new( self.signature.span(), "index getter requires exactly 2 arguments", - )), + )) + } // 4b. Index getters must return a value. - FnSpecialAccess::Index(Index::Get) if self.return_type().is_none() => + FnSpecialAccess::Index(Index::Get) if self.return_type().is_none() => { return Err(syn::Error::new( self.signature.span(), - "index getter must return a value" - )), + "index getter must return a value", + )) + } // 5a. Index setters must take the subject, "index", and new value as arguments. - FnSpecialAccess::Index(Index::Set) if self.arg_count() != 3 => + FnSpecialAccess::Index(Index::Set) if self.arg_count() != 3 => { return Err(syn::Error::new( self.signature.span(), "index setter requires exactly 3 arguments", - )), + )) + } // 5b. Index setters must return nothing. - FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => + FnSpecialAccess::Index(Index::Set) if self.return_type().is_some() => { return Err(syn::Error::new( self.signature.span(), - "index setter must return no value" - )), + "index setter must return no value", + )) + } _ => {} } @@ -685,7 +727,7 @@ impl ExportedFn { quote! { impl PluginFunction for #type_name { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), #arg_count, "wrong arg count: {} != {}", diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 31797438..61c1f942 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -279,7 +279,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 0usize, "wrong arg count: {} != {}", args.len(), 0usize); @@ -323,7 +323,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -364,7 +364,7 @@ mod generate_tests { let expected_tokens = quote! { impl PluginFunction for MyType { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -398,7 +398,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -445,7 +445,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -493,7 +493,7 @@ mod generate_tests { struct Token(); impl PluginFunction for Token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 4d71f296..59e58ce7 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -307,7 +307,7 @@ mod generate_tests { struct get_mystic_number_token(); impl PluginFunction for get_mystic_number_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 0usize, "wrong arg count: {} != {}", args.len(), 0usize); @@ -369,7 +369,7 @@ mod generate_tests { struct add_one_to_token(); impl PluginFunction for add_one_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -446,7 +446,7 @@ mod generate_tests { struct add_one_to_token(); impl PluginFunction for add_one_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -474,7 +474,7 @@ mod generate_tests { struct add_n_to_token(); impl PluginFunction for add_n_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -540,7 +540,7 @@ mod generate_tests { struct add_together_token(); impl PluginFunction for add_together_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -613,7 +613,7 @@ mod generate_tests { struct add_together_token(); impl PluginFunction for add_together_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -855,7 +855,7 @@ mod generate_tests { struct get_mystic_number_token(); impl PluginFunction for get_mystic_number_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 0usize, "wrong arg count: {} != {}", args.len(), 0usize); @@ -948,7 +948,7 @@ mod generate_tests { struct print_out_to_token(); impl PluginFunction for print_out_to_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1012,7 +1012,7 @@ mod generate_tests { struct increment_token(); impl PluginFunction for increment_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1079,7 +1079,7 @@ mod generate_tests { struct increment_token(); impl PluginFunction for increment_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1166,7 +1166,7 @@ mod generate_tests { struct increment_token(); impl PluginFunction for increment_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1251,7 +1251,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1317,7 +1317,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); @@ -1383,7 +1383,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1454,7 +1454,7 @@ mod generate_tests { struct int_foo_token(); impl PluginFunction for int_foo_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1521,7 +1521,7 @@ mod generate_tests { struct get_by_index_token(); impl PluginFunction for get_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1593,7 +1593,7 @@ mod generate_tests { struct get_by_index_token(); impl PluginFunction for get_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 2usize, "wrong arg count: {} != {}", args.len(), 2usize); @@ -1662,7 +1662,7 @@ mod generate_tests { struct set_by_index_token(); impl PluginFunction for set_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 3usize, "wrong arg count: {} != {}", args.len(), 3usize); @@ -1738,7 +1738,7 @@ mod generate_tests { struct set_by_index_token(); impl PluginFunction for set_by_index_token { fn call(&self, - args: &mut [&mut Dynamic], pos: Position + args: &mut [&mut Dynamic] ) -> Result> { debug_assert_eq!(args.len(), 3usize, "wrong arg count: {} != {}", args.len(), 3usize); diff --git a/src/fn_call.rs b/src/fn_call.rs index 3728d4e8..c40d352f 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -232,7 +232,7 @@ impl Engine { // Run external function let result = if func.is_plugin_fn() { - func.get_plugin_fn().call(args, Position::none()) + func.get_plugin_fn().call(args) } else { func.get_native_fn()(self, lib, args) }; @@ -1099,7 +1099,7 @@ impl Engine { self.call_script_fn(scope, mods, state, lib, &mut None, name, func, args, level) } - Some(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut(), Position::none()), + Some(f) if f.is_plugin_fn() => f.get_plugin_fn().call(args.as_mut()), Some(f) if f.is_native() => { if !f.is_method() { // Clone first argument diff --git a/src/plugin.rs b/src/plugin.rs index ef4a6590..e6e4e956 100644 --- a/src/plugin.rs +++ b/src/plugin.rs @@ -3,7 +3,7 @@ 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, Position, RegisterResultFn, + Engine, EvalAltResult, FnAccess, ImmutableString, Module, RegisterResultFn, }; #[cfg(not(features = "no_module"))] @@ -34,8 +34,7 @@ pub trait PluginFunction { fn is_method_call(&self) -> bool; fn is_varadic(&self) -> bool; - fn call(&self, args: &mut [&mut Dynamic], pos: Position) - -> Result>; + fn call(&self, args: &mut [&mut Dynamic]) -> Result>; fn clone_boxed(&self) -> Box; From 5a7a0f11ae504a371bb0dbd72b3f7b0205396881 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Fri, 18 Sep 2020 14:12:57 +0800 Subject: [PATCH 28/34] Fix doc test. --- src/fn_register.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/fn_register.rs b/src/fn_register.rs index 28d33198..12aedad2 100644 --- a/src/fn_register.rs +++ b/src/fn_register.rs @@ -46,7 +46,7 @@ pub trait RegisterPlugin { /// fn is_method_call(&self) -> bool { false } /// fn is_varadic(&self) -> bool { false } /// - /// fn call(&self, args: &mut[&mut Dynamic], pos: Position) -> Result> { + /// fn call(&self, args: &mut[&mut Dynamic]) -> Result> { /// let x1: NUMBER = std::mem::take(args[0]).clone().cast::(); /// let y1: NUMBER = std::mem::take(args[1]).clone().cast::(); /// let x2: NUMBER = std::mem::take(args[2]).clone().cast::(); From bf245a798b57b9776eacffba8926b55f248391af Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sat, 19 Sep 2020 18:18:40 +0800 Subject: [PATCH 29/34] Enable String parameters. --- codegen/src/function.rs | 23 ++++++++--- codegen/src/rhai_module.rs | 6 +++ codegen/src/test/function.rs | 12 ++---- codegen/src/test/module.rs | 76 ++++++++++++++++++++++++++++++++---- tests/plugins.rs | 5 +++ 5 files changed, 100 insertions(+), 22 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index aa059013..82e76214 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -638,11 +638,13 @@ impl ExportedFn { // Handle the rest of the arguments, which all are passed by value. // // The only exception is strings, which need to be downcast to ImmutableString to enable a - // zero-copy conversion to &str by reference. + // zero-copy conversion to &str by reference, or a cloned String. let str_type_path = syn::parse2::(quote! { str }).unwrap(); + let string_type_path = syn::parse2::(quote! { String }).unwrap(); for (i, arg) in self.arg_list().enumerate().skip(skip_first_arg as usize) { let var = syn::Ident::new(&format!("arg{}", i), proc_macro2::Span::call_site()); - let is_str_ref; + let is_string; + let is_ref; match arg { syn::FnArg::Typed(pattern) => { let arg_type: &syn::Type = pattern.ty.as_ref(); @@ -653,15 +655,24 @@ impl ExportedFn { .. }) => match elem.as_ref() { &syn::Type::Path(ref p) if p.path == str_type_path => { - is_str_ref = true; + is_string = true; + is_ref = true; quote_spanned!(arg_type.span()=> mem::take(args[#i]) .clone().cast::()) } _ => panic!("internal error: why wasn't this found earlier!?"), }, + &syn::Type::Path(ref p) if p.path == string_type_path => { + is_string = true; + is_ref = false; + quote_spanned!(arg_type.span()=> + mem::take(args[#i]) + .clone().cast::<#arg_type>()) + } _ => { - is_str_ref = false; + is_string = false; + is_ref = false; quote_spanned!(arg_type.span()=> mem::take(args[#i]).clone().cast::<#arg_type>()) } @@ -673,7 +684,7 @@ impl ExportedFn { }) .unwrap(), ); - if !is_str_ref { + if !is_string { input_type_exprs.push( syn::parse2::(quote_spanned!( arg_type.span()=> TypeId::of::<#arg_type>() @@ -691,7 +702,7 @@ impl ExportedFn { } syn::FnArg::Receiver(_) => panic!("internal error: how did this happen!?"), } - if !is_str_ref { + if !is_ref { unpack_exprs.push(syn::parse2::(quote! { #var }).unwrap()); } else { unpack_exprs.push(syn::parse2::(quote! { &#var }).unwrap()); diff --git a/codegen/src/rhai_module.rs b/codegen/src/rhai_module.rs index 1859ba42..c25c9484 100644 --- a/codegen/src/rhai_module.rs +++ b/codegen/src/rhai_module.rs @@ -19,6 +19,7 @@ pub(crate) fn generate_body( let mut add_mod_blocks: Vec = Vec::new(); let mut set_flattened_mod_blocks: Vec = Vec::new(); let str_type_path = syn::parse2::(quote! { str }).unwrap(); + let string_type_path = syn::parse2::(quote! { String }).unwrap(); for (const_name, _, _) in consts { let const_literal = syn::LitStr::new(&const_name, proc_macro2::Span::call_site()); @@ -97,6 +98,11 @@ pub(crate) fn generate_body( } _ => panic!("internal error: non-string shared reference!?"), }, + syn::Type::Path(ref p) if p.path == string_type_path => { + syn::parse2::(quote! { + ImmutableString }) + .unwrap() + } syn::Type::Reference(syn::TypeReference { mutability: Some(_), ref elem, diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index 61c1f942..b6d8e894 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -245,8 +245,7 @@ mod generate_tests { false } }); - /* - let (actual_diff, expected_diff) = { + let (_actual_diff, _expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); for (a, e) in _iter.take(50) { @@ -255,13 +254,10 @@ mod generate_tests { } (actual_diff, expected_diff) }; - */ eprintln!("actual != expected, diverge at char {}", counter); - /* - eprintln!(" actual: {}", actual_diff); - eprintln!("expected: {}", expected_diff); - assert!(false); - */ + // eprintln!(" actual: {}", _actual_diff); + // eprintln!("expected: {}", _expected_diff); + // assert!(false); } assert_eq!(actual, expected); } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index 59e58ce7..a2ab7eab 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -226,8 +226,7 @@ mod generate_tests { false } }); - /* - let (actual_diff, expected_diff) = { + let (_actual_diff, _expected_diff) = { let mut actual_diff = String::new(); let mut expected_diff = String::new(); for (a, e) in _iter.take(50) { @@ -236,13 +235,10 @@ mod generate_tests { } (actual_diff, expected_diff) }; - */ eprintln!("actual != expected, diverge at char {}", counter); - /* - eprintln!(" actual: {}", actual_diff); - eprintln!("expected: {}", expected_diff); - assert!(false); - */ + // eprintln!(" actual: {}", _actual_diff); + // eprintln!("expected: {}", _expected_diff); + // assert!(false); } assert_eq!(actual, expected); } @@ -978,6 +974,70 @@ mod generate_tests { assert_streams_eq(item_mod.generate(), expected_tokens); } + #[test] + fn one_string_arg_fn_module() { + let input_tokens: TokenStream = quote! { + pub mod str_fn { + pub fn print_out_to(x: String) { + x + 1 + } + } + }; + + let expected_tokens = quote! { + pub mod str_fn { + pub fn print_out_to(x: String) { + 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("print_out_to", FnAccess::Public, + &[core::any::TypeId::of::()], + CallableFunction::from_plugin(print_out_to_token())); + if flatten {} else {} + } + #[allow(non_camel_case_types)] + struct print_out_to_token(); + impl PluginFunction for print_out_to_token { + fn call(&self, + args: &mut [&mut Dynamic] + ) -> Result> { + debug_assert_eq!(args.len(), 1usize, + "wrong arg count: {} != {}", args.len(), 1usize); + let arg0 = mem::take(args[0usize]).clone().cast::(); + Ok(Dynamic::from(print_out_to(arg0))) + } + + fn is_method_call(&self) -> bool { false } + fn is_varadic(&self) -> bool { false } + fn clone_boxed(&self) -> Box { + Box::new(print_out_to_token()) + } + fn input_types(&self) -> Box<[TypeId]> { + new_vec![TypeId::of::()].into_boxed_slice() + } + } + pub fn print_out_to_token_callable() -> CallableFunction { + CallableFunction::from_plugin(print_out_to_token()) + } + pub fn print_out_to_token_input_types() -> Box<[TypeId]> { + print_out_to_token().input_types() + } + } + }; + + let item_mod = syn::parse2::(input_tokens).unwrap(); + assert_streams_eq(item_mod.generate(), expected_tokens); + } + #[test] fn one_mut_ref_fn_module() { let input_tokens: TokenStream = quote! { diff --git a/tests/plugins.rs b/tests/plugins.rs index 2bee84e4..ce403dd6 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -24,6 +24,10 @@ mod test { } } + pub fn hash(_text: String) -> INT { + 42 + } + #[rhai_fn(name = "test", name = "hi")] #[inline(always)] pub fn len(array: &mut Array, mul: INT) -> INT { @@ -77,6 +81,7 @@ fn test_plugins_package() -> Result<(), Box> { #[cfg(not(feature = "no_object"))] assert_eq!(engine.eval::("let a = [1, 2, 3]; a.foo")?, 1); + assert_eq!(engine.eval::(r#"hash("hello")"#)?, 42); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; hi(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); From 2ba875a17728648de8155a603b1e16215e800850 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Sep 2020 14:23:14 +0800 Subject: [PATCH 30/34] Use take_string for &str and String parameters. --- codegen/src/function.rs | 6 ++---- codegen/src/test/function.rs | 2 +- codegen/src/test/module.rs | 4 ++-- src/any.rs | 4 +++- tests/plugins.rs | 4 ++++ 5 files changed, 12 insertions(+), 8 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 82e76214..2fca6d96 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -658,8 +658,7 @@ impl ExportedFn { is_string = true; is_ref = true; quote_spanned!(arg_type.span()=> - mem::take(args[#i]) - .clone().cast::()) + mem::take(args[#i]).take_immutable_string().unwrap()) } _ => panic!("internal error: why wasn't this found earlier!?"), }, @@ -667,8 +666,7 @@ impl ExportedFn { is_string = true; is_ref = false; quote_spanned!(arg_type.span()=> - mem::take(args[#i]) - .clone().cast::<#arg_type>()) + mem::take(args[#i]).take_string().unwrap()) } _ => { is_string = false; diff --git a/codegen/src/test/function.rs b/codegen/src/test/function.rs index b6d8e894..f22f1fe2 100644 --- a/codegen/src/test/function.rs +++ b/codegen/src/test/function.rs @@ -493,7 +493,7 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0 = mem::take(args[0usize]).clone().cast::(); + let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap(); Ok(Dynamic::from(special_print(&arg0))) } diff --git a/codegen/src/test/module.rs b/codegen/src/test/module.rs index a2ab7eab..80a93f28 100644 --- a/codegen/src/test/module.rs +++ b/codegen/src/test/module.rs @@ -948,7 +948,7 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0 = mem::take(args[0usize]).clone().cast::(); + let arg0 = mem::take(args[0usize]).take_immutable_string().unwrap(); Ok(Dynamic::from(print_out_to(&arg0))) } @@ -1012,7 +1012,7 @@ mod generate_tests { ) -> Result> { debug_assert_eq!(args.len(), 1usize, "wrong arg count: {} != {}", args.len(), 1usize); - let arg0 = mem::take(args[0usize]).clone().cast::(); + let arg0 = mem::take(args[0usize]).take_string().unwrap(); Ok(Dynamic::from(print_out_to(arg0))) } diff --git a/src/any.rs b/src/any.rs index 7b1fa184..a1e532aa 100644 --- a/src/any.rs +++ b/src/any.rs @@ -1136,6 +1136,7 @@ impl Dynamic { } /// Convert the `Dynamic` into `String` and return it. + /// If there are other references to the same string, a cloned copy is returned. /// Returns the name of the actual type if the cast fails. #[inline(always)] pub fn take_string(self) -> Result { @@ -1145,7 +1146,8 @@ impl Dynamic { /// Convert the `Dynamic` into `ImmutableString` and return it. /// Returns the name of the actual type if the cast fails. - pub(crate) fn take_immutable_string(self) -> Result { + #[inline] + pub fn take_immutable_string(self) -> Result { match self.0 { Union::Str(s) => Ok(s), Union::FnPtr(f) => Ok(f.take_data().0), diff --git a/tests/plugins.rs b/tests/plugins.rs index ce403dd6..909ad51b 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -27,6 +27,9 @@ mod test { pub fn hash(_text: String) -> INT { 42 } + pub fn hash2(_text: &str) -> INT { + 42 + } #[rhai_fn(name = "test", name = "hi")] #[inline(always)] @@ -82,6 +85,7 @@ fn test_plugins_package() -> Result<(), Box> { assert_eq!(engine.eval::("let a = [1, 2, 3]; a.foo")?, 1); assert_eq!(engine.eval::(r#"hash("hello")"#)?, 42); + assert_eq!(engine.eval::(r#"hash2("hello")"#)?, 42); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; hi(a, 2)")?, 6); assert_eq!(engine.eval::("let a = [1, 2, 3]; test(a, 2)")?, 6); From 3a98567139185f27d80d32b7e2c92907f81c4664 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Sep 2020 14:29:10 +0800 Subject: [PATCH 31/34] Use take_string. --- codegen/src/function.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/codegen/src/function.rs b/codegen/src/function.rs index 2c0cf525..2fca6d96 100644 --- a/codegen/src/function.rs +++ b/codegen/src/function.rs @@ -666,8 +666,7 @@ impl ExportedFn { is_string = true; is_ref = false; quote_spanned!(arg_type.span()=> - mem::take(args[#i]) - .clone().cast::<#arg_type>()) + mem::take(args[#i]).take_string().unwrap()) } _ => { is_string = false; From c7a675e18a271259b5fd43e980cc1fec2abec720 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Sep 2020 14:52:38 +0800 Subject: [PATCH 32/34] Fix no_object builds. --- RELEASES.md | 4 ++-- doc/src/rust/indexers.md | 2 +- src/api.rs | 17 +++++++++-------- src/module.rs | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 9d8476fb..ccba5394 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -74,7 +74,7 @@ New features * Currying of function pointers is supported via the new `curry` keyword. * Automatic currying of anonymous functions to capture shared variables from the external scope. * Capturing of the calling scope for function call via the `func!(...)` syntax. -* `Module::set_indexer_get_set_fn` is added as a shorthand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`. +* `Module::set_indexer_get_set_fn` is added as a short-hand of both `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn`. * New `unicode-xid-ident` feature to allow [Unicode Standard Annex #31](http://www.unicode.org/reports/tr31/) for identifiers. * `Scope::iter_raw` returns an iterator with a reference to the underlying `Dynamic` value (which may be shared). @@ -200,7 +200,7 @@ Breaking changes New features ------------ -* Indexers are now split into getters and setters (which now support updates). The API is split into `Engine::register_indexer_get` and `Engine::register_indexer_set` with `Engine::register_indexer_get_set` being a shorthand. Similarly, `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn` are added. +* Indexers are now split into getters and setters (which now support updates). The API is split into `Engine::register_indexer_get` and `Engine::register_indexer_set` with `Engine::register_indexer_get_set` being a short-hand. Similarly, `Module::set_indexer_get_fn` and `Module::set_indexer_set_fn` are added. * `Engine:register_fn` and `Engine:register_result_fn` accepts functions that take parameters of type `&str` (immutable string slice), which maps directly to `ImmutableString`. This is to avoid needing wrappers for functions taking string parameters. * Set maximum limit on data sizes: `Engine::set_max_string_size`, `Engine::set_max_array_size` and `Engine::set_max_map_size`. * Supports trailing commas on array literals, object map literals, function definitions and function calls. diff --git a/doc/src/rust/indexers.md b/doc/src/rust/indexers.md index 69f73965..107ed5c9 100644 --- a/doc/src/rust/indexers.md +++ b/doc/src/rust/indexers.md @@ -52,7 +52,7 @@ let mut engine = Engine::new(); engine .register_type::() .register_fn("new_ts", TestStruct::new) - // Shorthand: .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); + // Short-hand: .register_indexer_get_set(TestStruct::get_field, TestStruct::set_field); .register_indexer_get(TestStruct::get_field) .register_indexer_set(TestStruct::set_field); diff --git a/src/api.rs b/src/api.rs index 85e0aab3..16733f25 100644 --- a/src/api.rs +++ b/src/api.rs @@ -12,17 +12,18 @@ use crate::scope::Scope; use crate::token::{lex, Position}; #[cfg(not(feature = "no_index"))] -#[cfg(not(feature = "no_object"))] use crate::engine::{FN_IDX_GET, FN_IDX_SET}; #[cfg(not(feature = "no_object"))] use crate::{ engine::{make_getter, make_setter, Map}, error::ParseErrorType, - fn_register::{RegisterFn, RegisterResultFn}, token::Token, }; +#[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] +use crate::fn_register::{RegisterFn, RegisterResultFn}; + #[cfg(not(feature = "no_function"))] use crate::{ engine::get_script_function_by_signature, fn_args::FuncArgs, fn_call::ensure_no_data_race, @@ -375,7 +376,7 @@ impl Engine { self.register_result_fn(&make_setter(name), callback) } - /// Shorthand for registering both getter and setter functions + /// Short-hand for registering both getter and setter functions /// of a registered type with the `Engine`. /// /// All function signatures must start with `&mut self` and not `&self`. @@ -427,7 +428,7 @@ impl Engine { self.register_get(name, get_fn).register_set(name, set_fn) } - /// Register an index getter for a registered type with the `Engine`. + /// Register an index getter for a custom type with the `Engine`. /// /// The function signature must start with `&mut self` and not `&self`. /// @@ -476,7 +477,7 @@ impl Engine { self.register_fn(FN_IDX_GET, callback) } - /// Register an index getter for a registered type with the `Engine`. + /// Register an index getter for a custom type with the `Engine`. /// Returns `Result>`. /// /// The function signature must start with `&mut self` and not `&self`. @@ -527,7 +528,7 @@ impl Engine { self.register_result_fn(FN_IDX_GET, callback) } - /// Register an index setter for a registered type with the `Engine`. + /// Register an index setter for a custom type with the `Engine`. /// /// # Example /// @@ -575,7 +576,7 @@ impl Engine { self.register_fn(FN_IDX_SET, callback) } - /// Register an index setter for a registered type with the `Engine`. + /// Register an index setter for a custom type with the `Engine`. /// Returns `Result>`. /// /// # Example @@ -627,7 +628,7 @@ impl Engine { self.register_result_fn(FN_IDX_SET, callback) } - /// Shorthand for register both index getter and setter functions for a registered type with the `Engine`. + /// Short-hand for register both index getter and setter functions for a custom type with the `Engine`. /// /// # Example /// diff --git a/src/module.rs b/src/module.rs index 51d5539c..51bfc3bf 100644 --- a/src/module.rs +++ b/src/module.rs @@ -808,7 +808,7 @@ impl Module { } /// Set a pair of Rust index getter and setter functions, returning both hash keys. - /// This is a shorthand for `set_indexer_get_fn` and `set_indexer_set_fn`. + /// This is a short-hand for `set_indexer_get_fn` and `set_indexer_set_fn`. /// /// If there are similar existing Rust functions, they are replaced. /// From a49b58002a4a67517b6f2cc14431cdf510b80318 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Sep 2020 15:55:11 +0800 Subject: [PATCH 33/34] Fix doc tests. --- src/api.rs | 5 +++++ src/engine.rs | 4 +--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/api.rs b/src/api.rs index 16733f25..b2cc4b31 100644 --- a/src/api.rs +++ b/src/api.rs @@ -453,6 +453,7 @@ impl Engine { /// let mut engine = Engine::new(); /// /// // Register the custom type. + /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// /// engine.register_fn("new_ts", TestStruct::new); @@ -505,6 +506,7 @@ impl Engine { /// let mut engine = Engine::new(); /// /// // Register the custom type. + /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// /// engine.register_fn("new_ts", TestStruct::new); @@ -549,6 +551,7 @@ impl Engine { /// let mut engine = Engine::new(); /// /// // Register the custom type. + /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// /// engine.register_fn("new_ts", TestStruct::new); @@ -601,6 +604,7 @@ impl Engine { /// let mut engine = Engine::new(); /// /// // Register the custom type. + /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// /// engine.register_fn("new_ts", TestStruct::new); @@ -650,6 +654,7 @@ impl Engine { /// let mut engine = Engine::new(); /// /// // Register the custom type. + /// # #[cfg(not(feature = "no_object"))] /// engine.register_type::(); /// /// engine.register_fn("new_ts", TestStruct::new); diff --git a/src/engine.rs b/src/engine.rs index 66c4de23..ad5b1c0a 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1134,8 +1134,7 @@ impl Engine { ) -> Result, Box> { self.inc_operations(state)?; - #[cfg(not(feature = "no_index"))] - #[cfg(not(feature = "no_object"))] + #[cfg(any(not(feature = "no_index"), not(feature = "no_object")))] let is_ref = target.is_ref(); let val = target.as_mut(); @@ -1200,7 +1199,6 @@ impl Engine { } } - #[cfg(not(feature = "no_object"))] #[cfg(not(feature = "no_index"))] _ if _indexers => { let type_name = val.type_name(); From 44416ecb05181726e2e80de488945d583aad5b3f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Sun, 20 Sep 2020 18:33:52 +0800 Subject: [PATCH 34/34] Delete benchmark.yml --- .github/workflows/benchmark.yml | 29 ----------------------------- 1 file changed, 29 deletions(-) delete mode 100644 .github/workflows/benchmark.yml diff --git a/.github/workflows/benchmark.yml b/.github/workflows/benchmark.yml deleted file mode 100644 index df310705..00000000 --- a/.github/workflows/benchmark.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Benchmark -on: - push: - branches: - - master - -jobs: - benchmark: - name: Run Rust benchmark - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v2 - - run: rustup toolchain update nightly && rustup default nightly - - name: Run benchmark - run: cargo +nightly bench | tee output.txt - - name: Store benchmark result - uses: rhysd/github-action-benchmark@v1 - with: - name: Rust Benchmark - tool: 'cargo' - output-file-path: output.txt - # Use personal access token instead of GITHUB_TOKEN due to https://github.community/t5/GitHub-Actions/Github-action-not-triggering-gh-pages-upon-push/td-p/26869/highlight/false - github-token: ${{ secrets.RHAI }} - auto-push: true - # Show alert with commit comment on detecting possible performance regression - alert-threshold: '200%' - comment-on-alert: true - fail-on-alert: true - alert-comment-cc-users: '@schungx'