diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index f897b61e..c6457b52 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -43,7 +43,11 @@ The Rhai Scripting Language 4. [Printing Custom Types](rust/print-custom.md) 9. [Packages](rust/packages/index.md) 1. [Built-in Packages](rust/packages/builtin.md) - 2. [Create a Custom Package](rust/packages/create.md) + 2. [Load a Plugin Module as a Package](rust/packages/plugin.md) + 3. [Manually Create a Custom Package](rust/packages/create.md) + 10. [Plugins](plugins/index.md) + 1. [Create a Plugin Module](plugins/module.md) + 2. [Create a Plugin Function](plugins/function.md) 5. [Rhai Language Reference](language/index.md) 1. [Comments](language/comments.md) 2. [Values and Types](language/values-and-types.md) @@ -84,7 +88,7 @@ The Rhai Scripting Language 17. [Modules](language/modules/index.md) 1. [Export Variables, Functions and Sub-Modules](language/modules/export.md) 2. [Import Modules](language/modules/import.md) - 3. [Create from Rust](rust/modules/index.md) + 3. [Create from Rust](rust/modules/create.md) 4. [Create from AST](language/modules/ast.md) 5. [Module Resolvers](rust/modules/resolvers.md) 1. [Custom Implementation](rust/modules/imp-resolver.md) diff --git a/doc/src/language/num-fn.md b/doc/src/language/num-fn.md index a963c7e2..c9427a35 100644 --- a/doc/src/language/num-fn.md +++ b/doc/src/language/num-fn.md @@ -9,11 +9,11 @@ Integer Functions The following standard functions (defined in the [`BasicMathPackage`][packages] but excluded if using a [raw `Engine`]) operate on `i8`, `i16`, `i32`, `i64`, `f32` and `f64` only: -| Function | Description | -| ------------ | --------------------------------------------------------------- | -| `abs` | absolute value | -| `sign` | returns -1 if the number is negative, +1 if positive, 0 if zero | -| [`to_float`] | converts an integer type to `f64` | +| Function | Description | +| ------------ | ----------------------------------------------------------------------- | +| `abs` | absolute value | +| `sign` | returns -1 (`INT`) if the number is negative, +1 if positive, 0 if zero | +| [`to_float`] | converts an integer type to `FLOAT` | Floating-Point Functions ----------------------- diff --git a/doc/src/links.md b/doc/src/links.md index e030478a..1f666afc 100644 --- a/doc/src/links.md +++ b/doc/src/links.md @@ -33,6 +33,12 @@ [packages]: {{rootUrl}}/rust/packages/index.md [custom package]: {{rootUrl}}/rust/packages/create.md [custom packages]: {{rootUrl}}/rust/packages/create.md +[plugin]: {{rootUrl}}/plugins/index.md +[plugins]: {{rootUrl}}/plugins/index.md +[plugin module]: {{rootUrl}}/plugins/module.md +[plugin modules]: {{rootUrl}}/plugins/module.md +[plugin function]: {{rootUrl}}/plugins/function.md +[plugin functions]: {{rootUrl}}/plugins/function.md [`Scope`]: {{rootUrl}}/engine/scope.md [`serde`]: {{rootUrl}}/rust/serde.md diff --git a/doc/src/plugins/function.md b/doc/src/plugins/function.md new file mode 100644 index 00000000..f138d5c4 --- /dev/null +++ b/doc/src/plugins/function.md @@ -0,0 +1,78 @@ +Create a Plugin Function +======================== + +{{#include ../links.md}} + + +Sometimes only a few ad hoc functions are required and it is simpler to register +individual functions instead of a full-blown [plugin module]. + + +Macros +------ + +| Macro | Apply to | Behavior | +| ------------------------ | ------------------------------------------------------------- | --------------------------------------------------------- | +| `#[export_fn]` | Rust function defined in module | Export the function | +| `#[rhai_fn(return_raw)]` | Rust function returning `Result>` | Specify that this is a fallible function | +| `register_exported_fn!` | [`Engine`] instance, register name, function name | Register function with the [`Engine`] under specific name | +| `set_exported_fn!` | [`Module`], register name, function name | Register function with the [`Module`] under specific name | + + +`#[export_fn]` and `register_exported_fn!` +----------------------------------------- + +Apply `#[export_fn]` onto a function defined at _module level_ to convert it into a Rhai plugin function. + +The function cannot be nested inside another function - it can only be defined directly under a module. + +To register the plugin function, simply call `register_exported_fn!`. The name of the function can be +any text string, so it is possible to register _overloaded_ functions as well as operators. + +```rust +use rhai::plugins::*; // import macros + +#[export_fn] +fn increment(num: &mut i64) { + *num += 1; +} + +fn main() { + let mut engine = Engine::new(); + + // 'register_exported_fn!' registers the function as 'inc' with the Engine. + register_exported_fn!(engine, "inc", increment); +} +``` + + +Fallible Functions +------------------ + +To register [fallible functions] (i.e. functions that may return errors), apply the +`#[rhai_fn(return_raw)]` attribute on plugin functions that return `Result>`. + +A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does not +have the appropriate return type. + +```rust +use rhai::plugins::*; // import macros + +#[export_fn] +#[rhai_fn(return_raw)] +pub fn double_and_divide(x: i64, y: i64) -> Result> { + if y == 0 { + Err("Division by zero!".into()) + } else { + let result = (x * 2) / y; + Ok(result.into()) + } +} + +fn main() { + let mut engine = Engine::new(); + + // Overloads the operator '+' with the Engine. + register_exported_fn!(engine, "+", double_and_divide); +} +``` diff --git a/doc/src/plugins/index.md b/doc/src/plugins/index.md new file mode 100644 index 00000000..353dedd3 --- /dev/null +++ b/doc/src/plugins/index.md @@ -0,0 +1,11 @@ +Plugins +======= + +{{#include ../links.md}} + +Rhai contains a robust _plugin_ system that greatly simplifies registration of custom functions. + +Instead of the large `Engine::register_XXX` API, and the parallel `Module::set_fn_XXX` API, +a _plugin_ simplifies the work of creating and registering multiple functions into an [`Engine`]. + +Plugins are processed via a set of procedural macros under the `rhai::plugins` module. diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md new file mode 100644 index 00000000..ac558c61 --- /dev/null +++ b/doc/src/plugins/module.md @@ -0,0 +1,194 @@ +Create a Plugin Module +====================== + +{{#include ../links.md}} + + +The core of creating a plugin [module] is the `#[export_module]` attribute. + +When applied on a module definition, `#[export_module]` automatically generates Rhai-acceptable +functions from all `pub` functions defined within. + +The resulting module can then be loaded into an [`Engine`] as a normal [module], +or as a [custom package]. + + +Macros +------ + +| Macro | Apply to | Behavior | +| --------------------------- | ----------------------------------------------------------------------------- | ----------------------------------------------- | +| `#[export_module]` | Rust module | Export all `pub` functions | +| `#[rhai_fn(skip)]` | Function in Rust module | Do not export this function | +| `#[rhai_fn(return_raw)]` | `pub` function in Rust module returning `Result>` | Specify that this is a fallible function | +| `#[rhai_fn(name = "...")]` | `pub` function in Rust module | Register function under specific name | +| `#[rhai_fn(get = "...")]` | `pub` function in Rust module (first parameter must be `&mut`) | Register a property getter under specific name | +| `#[rhai_fn(set = "...")]` | `pub` function in Rust module (first parameter must be `&mut`) | Register a property setter under specific name | +| `#[rhai_fn(index_get]` | `pub` function in Rust module (first parameter must be `&mut`) | Register a index getter | +| `#[rhai_fn(index_set)]` | `pub` function in Rust module (first parameter must be `&mut`) | Register a index setter | +| `#[rhai_mod(name = "...")]` | `pub` sub-module in Rust module | Export the sub-module under specific name | +| `exported_module!` | Rust module name | Create a [module] containing exported functions | + + +`#[export_module]` and `exported_module!` +---------------------------------------- + +Apply `#[export_module]` onto a standard module to convert all `pub` functions +into Rhai plugin functions. + +```rust +use rhai::plugins::*; // import macros + +#[export_module] +mod my_module { + // This function will be registered as 'greet'. + pub fn greet(name: &str) -> String { + format!("hello, {}!", name) + } + // This function will be registered as 'get_num'. + pub fn get_num() -> i64 { + mystic_number() + } + // This function will be registered as 'increment'. + pub fn increment(num: &mut i64) { + *num += 1; + } + // This function is NOT registered. + fn mystic_number() -> i64 { + 42 + } +} + +fn main() { + let mut engine = Engine::new(); + + // 'exported_module!' creates the plugin module. + let module = exported_module!(my_module); + + // A module can simply be loaded as a custom package. + engine.load_package(module); +} +``` + +The above automatically defines a plugin module named `my_module` which can be converted into +a Rhai [module] via `exported_module!`. The functions contained within the module definition +(i.e. `greet`, `get_num` and `increment`) are automatically registered into the [`Engine`] when +`Engine::load_package` is called. + +```rust +let x = greet("world"); +x == "hello, world!"; + +let x = greet(get_num().to_string()); +x == "hello, 42!"; + +let x = get_num(); +x == 42; + +increment(x); +x == 43; +``` + + +Getters, Setters and Indexers +----------------------------- + +Functions can be marked as [getters/setters] and [indexers] for [custom types] via the `#[rhai_fn]` +attribute, which is applied on a function level. + +```rust +use rhai::plugins::*; // import macros + +#[export_module] +mod my_module { + // This is a normal function 'greet'. + pub fn greet(name: &str) -> String { + format!("hello, {}!", name) + } + // This is a getter for 'MyType::prop'. + #[rhai_fn(get = "prop")] + pub fn get_prop(obj: &mut MyType) -> i64 { + obj.prop + } + // This is a setter for 'MyType::prop'. + #[rhai_fn(set = "prop")] + pub fn set_prop(obj: &mut MyType, value: i64) { + obj.prop = value; + } + // This is an index getter for 'MyType'. + #[rhai_fn(index_get)] + pub fn get_index(obj: &mut MyType, index: i64) -> bool { + obj.list[index] + } + // This is an index setter for 'MyType'. + #[rhai_fn(index_get)] + pub fn get_index(obj: &mut MyType, index: i64, state: bool) { + obj.list[index] = state; + } +} +``` + + +Function Overloading and Operators +--------------------------------- + +Operators and overloaded functions can be specified via `#[rhai_fn(name = "...")]` applied upon +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. + +Operators (which require function names that are not valid for Rust) can also be registered this way. + +```rust +use rhai::plugins::*; // import macros + +#[export_module] +mod my_module { + // This is the '+' operator for 'MyType'. + #[rhai_fn(name = "+")] + pub fn add(obj: &mut MyType, value: i64) { + obj.prop += value; + } + // This function is 'calc (i64)'. + #[rhai_fn(name = "calc")] + pub fn calc_with_default(num: i64) -> i64 { + ... + } + // This function is 'calc (i64, bool)'. + #[rhai_fn(name = "calc")] + pub fn calc_with_option(num: i64, option: bool) -> i64 { + ... + } +} +``` + + +Fallible Functions +------------------ + +To register [fallible functions] (i.e. functions that may return errors), apply the +`#[rhai_fn(return_raw)]` attribute on functions that return `Result>`. + +A syntax error is generated if the function with `#[rhai_fn(return_raw)]` does not +have the appropriate return type. + +```rust +use rhai::plugins::*; // import macros + +#[export_module] +mod my_module { + // This overloads the '/' operator for i64. + #[rhai_fn(name = "/", return_raw)] + pub fn double_and_divide(x: i64, y: i64) -> Result> { + if y == 0 { + Err("Division by zero!".into()) + } else { + let result = (x * 2) / y; + Ok(result.into()) + } + } +} +``` diff --git a/doc/src/rust/modules/index.md b/doc/src/rust/modules/create.md similarity index 81% rename from doc/src/rust/modules/index.md rename to doc/src/rust/modules/create.md index be6576bb..26441067 100644 --- a/doc/src/rust/modules/index.md +++ b/doc/src/rust/modules/create.md @@ -3,13 +3,23 @@ Create a Module from Rust {{#include ../../links.md}} -Manually creating a [`Module`] is possible via the `Module` API. + +Create via Plugin +----------------- + +By far the simplest way to create a [module] is via a [plugin module]. + + +Create via `Module` API +----------------------- + +Manually creating a [module] is possible via the `Module` API. For the complete `Module` API, refer to the [documentation](https://docs.rs/rhai/{{version}}/rhai/struct.Module.html) online. -Make the Module Available to the Engine --------------------------------------- +Make the `Module` Available to the `Engine` +------------------------------------------ In order to _use_ a custom module, there must be a [module resolver]. diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 89f0e306..d212d38f 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -1,5 +1,5 @@ -Create a Custom Package -====================== +Manually Create a Custom Package +=============================== {{#include ../../links.md}} @@ -13,7 +13,7 @@ Loading a package into an [`Engine`] is functionally equivalent to calling `Engi on _each_ of the functions inside the package. But because packages are _shared_, loading an existing package is _much_ cheaper than registering all the functions one by one. -The macro `rhai::def_package!` is used to create a new custom package. +The macro `rhai::def_package!` can be used to create a new custom package. Macro Parameters @@ -53,3 +53,48 @@ 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]. + +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. + +```rust +// Import necessary types and traits. +use rhai::{ + def_package, + packages::Package, + packages::{ArithmeticPackage, BasicArrayPackage, BasicMapPackage, LogicPackage} +}; +use rhai::plugin::*; + +// Define plugin module. +#[export_module] +mod my_module { + pub fn greet(name: &str) -> String { + format!("hello, {}!", name) + } + pub fn get_num() -> i64 { + 42 + } +} + +// Define the package 'MyPackage'. +def_package!(rhai:MyPackage:"My own personal super package", module, { + // Aggregate existing packages simply by calling 'init' on each. + ArithmeticPackage::init(module); + LogicPackage::init(module); + BasicArrayPackage::init(module); + BasicMapPackage::init(module); + + // Merge the plugin module into the custom package. + module.merge_flatten(exported_module!(my_module)); +}); +``` diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index 0b41656f..ee0daa37 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -11,7 +11,7 @@ packages to be used. Packages typically contain Rust functions that are callable within a Rhai script. All functions registered in a package is loaded under the _global namespace_ (i.e. they're available without module qualifiers). -Once a package is created (e.g. via `new`), it can be _shared_ (via `get`) among multiple instances of [`Engine`], +Once a package is created (e.g. via `Package::new`), it can be _shared_ (via `Package::get`) among multiple instances of [`Engine`], even across threads (under [`sync`]). Therefore, a package only has to be created _once_. ```rust @@ -36,3 +36,21 @@ 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). + + +Load a Module as a Package +-------------------------- + +Stand-alone [modules] can be loaded directly into an [`Engine`] as a package via the same `Engine::load_package` API. + +```rust +let mut module = Module::new(); + : + // add functions into module + : + +engine.load_package(module); +``` + +[Modules], however, are not _shared_, so use a [custom package] if it must be shared among multiple +instances of [`Engine`]. diff --git a/doc/src/rust/packages/plugin.md b/doc/src/rust/packages/plugin.md new file mode 100644 index 00000000..42bc9519 --- /dev/null +++ b/doc/src/rust/packages/plugin.md @@ -0,0 +1,41 @@ +Load a Plugin Module as a Package +================================ + +{{#include ../../links.md}} + +[Plugin modules] can be loaded as a package just like a normal [module]. + +```rust +use rhai::Engine; +use rhai::plugin::*; + +// Define plugin module. +#[export_module] +mod my_module { + pub fn greet(name: &str) -> String { + format!("hello, {}!", name) + } + pub fn get_num() -> i64 { + 42 + } +} + +fn main() { + let mut engine = Engine::new(); + + // Create plugin module. + let module = exported_module!(my_module); + + // Make the 'greet' and 'get_num' functions available to scripts. + engine.load_package(module); +} +``` + + +Share a Package Among `Engine`s +------------------------------ + +Loading a [module] via `Engine::load_package` consumes the module and it cannot be used again. + +If the functions are needed for multiple instances of [`Engine`], create a [custom package] from the +[plugin module], which can then be shared with `Package::get`. diff --git a/src/packages/arithmetic.rs b/src/packages/arithmetic.rs index b9553cd8..33b01b57 100644 --- a/src/packages/arithmetic.rs +++ b/src/packages/arithmetic.rs @@ -17,250 +17,246 @@ use crate::stdlib::format; macro_rules! gen_arithmetic_functions { ($root:ident => $($arg_type:ident),+) => { - pub mod $root { $( - pub mod $arg_type { - use super::super::*; + pub mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_module] - pub mod functions { - #[rhai_fn(name = "+", return_raw)] - #[inline] - pub fn add(x: $arg_type, y: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.checked_add(y).ok_or_else(|| { + #[export_module] + pub mod functions { + #[rhai_fn(name = "+", return_raw)] + #[inline] + pub fn add(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_add(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Addition overflow: {} + {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x + y)) + } + } + #[rhai_fn(name = "-", return_raw)] + #[inline] + pub fn subtract(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_sub(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Subtraction overflow: {} - {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x - y)) + } + } + #[rhai_fn(name = "*", return_raw)] + #[inline] + pub fn multiply(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_mul(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Multiplication overflow: {} * {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x * y)) + } + } + #[rhai_fn(name = "/", return_raw)] + #[inline] + pub fn divide(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + // Detect division by zero + if y == 0 { + EvalAltResult::ErrorArithmetic( + format!("Division by zero: {} / {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_div(y).ok_or_else(|| { EvalAltResult::ErrorArithmetic( - format!("Addition overflow: {} + {}", x, y), + format!("Division overflow: {} / {}", x, y), Position::none(), ) .into() }).map(Dynamic::from) - } else { - Ok(Dynamic::from(x + y)) } + } else { + Ok(Dynamic::from(x / y)) } - #[rhai_fn(name = "-", return_raw)] - #[inline] - pub fn subtract(x: $arg_type, y: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.checked_sub(y).ok_or_else(|| { + } + #[rhai_fn(name = "%", return_raw)] + #[inline] + pub fn modulo(x: $arg_type, y: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_rem(y).ok_or_else(|| { + EvalAltResult::ErrorArithmetic( + format!("Modulo division by zero or overflow: {} % {}", x, y), + Position::none(), + ) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x % y)) + } + } + #[rhai_fn(name = "~", return_raw)] + #[inline] + pub fn power(x: INT, y: INT) -> Result> { + if cfg!(not(feature = "unchecked")) { + if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { + EvalAltResult::ErrorArithmetic( + format!("Integer raised to too large an index: {} ~ {}", x, y), + Position::none(), + ) + .into() + } else if y < 0 { + EvalAltResult::ErrorArithmetic( + format!("Integer raised to a negative index: {} ~ {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_pow(y as u32).ok_or_else(|| { EvalAltResult::ErrorArithmetic( - format!("Subtraction overflow: {} - {}", x, y), + format!("Power overflow: {} ~ {}", x, y), Position::none(), ) .into() }).map(Dynamic::from) - } else { - Ok(Dynamic::from(x - y)) - } - } - #[rhai_fn(name = "*", return_raw)] - #[inline] - pub fn multiply(x: $arg_type, y: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.checked_mul(y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Multiplication overflow: {} * {}", x, y), - Position::none(), - ) - .into() - }).map(Dynamic::from) - } else { - Ok(Dynamic::from(x * y)) - } - } - #[rhai_fn(name = "/", return_raw)] - #[inline] - pub fn divide(x: $arg_type, y: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - // Detect division by zero - if y == 0 { - EvalAltResult::ErrorArithmetic( - format!("Division by zero: {} / {}", x, y), - Position::none(), - ) - .into() - } else { - x.checked_div(y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Division overflow: {} / {}", x, y), - Position::none(), - ) - .into() - }).map(Dynamic::from) - } - } else { - Ok(Dynamic::from(x / y)) - } - } - #[rhai_fn(name = "%", return_raw)] - #[inline] - pub fn modulo(x: $arg_type, y: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.checked_rem(y).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Modulo division by zero or overflow: {} % {}", x, y), - Position::none(), - ) - .into() - }).map(Dynamic::from) - } else { - Ok(Dynamic::from(x % y)) - } - } - #[rhai_fn(name = "~", return_raw)] - #[inline] - pub fn power(x: INT, y: INT) -> Result> { - if cfg!(not(feature = "unchecked")) { - if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { - EvalAltResult::ErrorArithmetic( - format!("Integer raised to too large an index: {} ~ {}", x, y), - Position::none(), - ) - .into() - } else if y < 0 { - EvalAltResult::ErrorArithmetic( - format!("Integer raised to a negative index: {} ~ {}", x, y), - Position::none(), - ) - .into() - } else { - x.checked_pow(y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Power overflow: {} ~ {}", x, y), - Position::none(), - ) - .into() - }).map(Dynamic::from) - } - } else { - Ok(Dynamic::from(x.pow(y as u32))) } + } else { + Ok(Dynamic::from(x.pow(y as u32))) } + } - #[rhai_fn(name = "<<", return_raw)] - #[inline] - pub fn shift_left(x: $arg_type, y: INT) -> Result> { - if cfg!(not(feature = "unchecked")) { - if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { + #[rhai_fn(name = "<<", return_raw)] + #[inline] + pub fn shift_left(x: $arg_type, y: INT) -> Result> { + if cfg!(not(feature = "unchecked")) { + if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { + EvalAltResult::ErrorArithmetic( + format!("Left-shift by too many bits: {} << {}", x, y), + Position::none(), + ) + .into() + } else if y < 0 { + EvalAltResult::ErrorArithmetic( + format!("Left-shift by a negative number: {} << {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_shl(y as u32).ok_or_else(|| { EvalAltResult::ErrorArithmetic( format!("Left-shift by too many bits: {} << {}", x, y), Position::none(), ) .into() - } else if y < 0 { - EvalAltResult::ErrorArithmetic( - format!("Left-shift by a negative number: {} << {}", x, y), - Position::none(), - ) - .into() - } else { - x.checked_shl(y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Left-shift by too many bits: {} << {}", x, y), - Position::none(), - ) - .into() - }).map(Dynamic::from) - } - } else { - Ok(Dynamic::from(x << y)) + }).map(Dynamic::from) } + } else { + Ok(Dynamic::from(x << y)) } - #[rhai_fn(name = ">>", return_raw)] - #[inline] - pub fn shift_right(x: $arg_type, y: INT) -> Result> { - if cfg!(not(feature = "unchecked")) { - if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { + } + #[rhai_fn(name = ">>", return_raw)] + #[inline] + pub fn shift_right(x: $arg_type, y: INT) -> Result> { + if cfg!(not(feature = "unchecked")) { + if cfg!(not(feature = "only_i32")) && y > (u32::MAX as INT) { + EvalAltResult::ErrorArithmetic( + format!("Right-shift by too many bits: {} >> {}", x, y), + Position::none(), + ) + .into() + } else if y < 0 { + EvalAltResult::ErrorArithmetic( + format!("Right-shift by a negative number: {} >> {}", x, y), + Position::none(), + ) + .into() + } else { + x.checked_shr(y as u32).ok_or_else(|| { EvalAltResult::ErrorArithmetic( format!("Right-shift by too many bits: {} >> {}", x, y), Position::none(), ) .into() - } else if y < 0 { - EvalAltResult::ErrorArithmetic( - format!("Right-shift by a negative number: {} >> {}", x, y), - Position::none(), - ) - .into() - } else { - x.checked_shr(y as u32).ok_or_else(|| { - EvalAltResult::ErrorArithmetic( - format!("Right-shift by too many bits: {} >> {}", x, y), - Position::none(), - ) - .into() - }).map(Dynamic::from) - } - } else { - Ok(Dynamic::from(x >> y)) + }).map(Dynamic::from) } - } - #[rhai_fn(name = "&")] - #[inline(always)] - pub fn binary_and(x: $arg_type, y: $arg_type) -> $arg_type { - x & y - } - #[rhai_fn(name = "|")] - #[inline(always)] - pub fn binary_or(x: $arg_type, y: $arg_type) -> $arg_type { - x | y - } - #[rhai_fn(name = "^")] - #[inline(always)] - pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type { - x ^ y + } else { + Ok(Dynamic::from(x >> y)) } } + #[rhai_fn(name = "&")] + #[inline(always)] + pub fn binary_and(x: $arg_type, y: $arg_type) -> $arg_type { + x & y + } + #[rhai_fn(name = "|")] + #[inline(always)] + pub fn binary_or(x: $arg_type, y: $arg_type) -> $arg_type { + x | y + } + #[rhai_fn(name = "^")] + #[inline(always)] + pub fn binary_xor(x: $arg_type, y: $arg_type) -> $arg_type { + x ^ y + } } - )* } + })* } } } macro_rules! gen_signed_functions { ($root:ident => $($arg_type:ident),+) => { - pub mod $root { $( - pub mod $arg_type { - use super::super::*; + pub mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_module] - pub mod functions { - #[rhai_fn(name = "-", return_raw)] - #[inline] - pub fn neg(x: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.checked_neg().ok_or_else(|| { - EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) - .into() - }).map(Dynamic::from) - } else { - Ok(Dynamic::from(-x)) - } + #[export_module] + pub mod functions { + #[rhai_fn(name = "-", return_raw)] + #[inline] + pub fn neg(x: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_neg().ok_or_else(|| { + EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(-x)) } - #[rhai_fn(return_raw)] - #[inline] - pub fn abs(x: $arg_type) -> Result> { - if cfg!(not(feature = "unchecked")) { - x.checked_abs().ok_or_else(|| { - EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) - .into() - }).map(Dynamic::from) - } else { - Ok(Dynamic::from(x.abs())) - } + } + #[rhai_fn(return_raw)] + #[inline] + pub fn abs(x: $arg_type) -> Result> { + if cfg!(not(feature = "unchecked")) { + x.checked_abs().ok_or_else(|| { + EvalAltResult::ErrorArithmetic(format!("Negation overflow: -{}", x), Position::none()) + .into() + }).map(Dynamic::from) + } else { + Ok(Dynamic::from(x.abs())) } - #[inline] - pub fn sign(x: $arg_type) -> INT { - if x == 0 { - 0 - } else if x < 0 { - -1 - } else { - 1 - } + } + #[inline] + pub fn sign(x: $arg_type) -> INT { + if x == 0 { + 0 + } else if x < 0 { + -1 + } else { + 1 } } } - )* } + })* } } } diff --git a/src/packages/array_basic.rs b/src/packages/array_basic.rs index a69372ae..66c35924 100644 --- a/src/packages/array_basic.rs +++ b/src/packages/array_basic.rs @@ -23,30 +23,33 @@ pub type Unit = (); macro_rules! gen_array_functions { ($root:ident => $($arg_type:ident),+ ) => { - pub mod $root { $( - pub mod $arg_type { - use super::super::*; + pub mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_fn] - #[inline(always)] - pub fn push_func(list: &mut Array, item: $arg_type) { - super::super::push(list, item); - } + #[export_fn] + #[inline(always)] + pub fn push(list: &mut Array, item: $arg_type) { + list.push(Dynamic::from(item)); + } - #[export_fn] - #[inline(always)] - pub fn insert_func(list: &mut Array, len: INT, item: $arg_type) { - super::super::insert(list, len, item); + #[export_fn] + pub fn insert(list: &mut Array, position: INT, item: $arg_type) { + if position <= 0 { + list.insert(0, Dynamic::from(item)); + } else if (position as usize) >= list.len() - 1 { + push(list, item); + } else { + list.insert(position as usize, Dynamic::from(item)); } } - )* } + })* } } } macro_rules! reg_functions { ($mod_name:ident += $root:ident ; $($arg_type:ident),+) => { $( - set_exported_fn!($mod_name, "push", $root::$arg_type::push_func); - set_exported_fn!($mod_name, "insert", $root::$arg_type::insert_func); + set_exported_fn!($mod_name, "push", $root::$arg_type::push); + set_exported_fn!($mod_name, "insert", $root::$arg_type::insert); $mod_name.set_raw_fn("pad", &[TypeId::of::(), TypeId::of::(), TypeId::of::<$arg_type>()], @@ -138,20 +141,6 @@ mod array_functions { } } -// Register array utility functions -#[inline(always)] -fn push(list: &mut Array, item: T) { - list.push(Dynamic::from(item)); -} -fn insert(list: &mut Array, position: INT, item: T) { - if position <= 0 { - list.insert(0, Dynamic::from(item)); - } else if (position as usize) >= list.len() - 1 { - push(list, item); - } else { - list.insert(position as usize, Dynamic::from(item)); - } -} fn pad( _engine: &Engine, _: &Module, diff --git a/src/packages/logic.rs b/src/packages/logic.rs index e05d8515..48e46047 100644 --- a/src/packages/logic.rs +++ b/src/packages/logic.rs @@ -3,45 +3,43 @@ use crate::plugin::*; macro_rules! gen_cmp_functions { ($root:ident => $($arg_type:ident),+) => { - mod $root { $( - pub mod $arg_type { - use crate::plugin::*; + mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_module] - pub mod functions { - #[rhai_fn(name = "<")] - #[inline(always)] - pub fn lt(x: $arg_type, y: $arg_type) -> bool { - x < y - } - #[rhai_fn(name = "<=")] - #[inline(always)] - pub fn lte(x: $arg_type, y: $arg_type) -> bool { - x <= y - } - #[rhai_fn(name = ">")] - #[inline(always)] - pub fn gt(x: $arg_type, y: $arg_type) -> bool { - x > y - } - #[rhai_fn(name = ">=")] - #[inline(always)] - pub fn gte(x: $arg_type, y: $arg_type) -> bool { - x >= y - } - #[rhai_fn(name = "==")] - #[inline(always)] - pub fn eq(x: $arg_type, y: $arg_type) -> bool { - x == y - } - #[rhai_fn(name = "!=")] - #[inline(always)] - pub fn ne(x: $arg_type, y: $arg_type) -> bool { - x != y - } + #[export_module] + pub mod functions { + #[rhai_fn(name = "<")] + #[inline(always)] + pub fn lt(x: $arg_type, y: $arg_type) -> bool { + x < y + } + #[rhai_fn(name = "<=")] + #[inline(always)] + pub fn lte(x: $arg_type, y: $arg_type) -> bool { + x <= y + } + #[rhai_fn(name = ">")] + #[inline(always)] + pub fn gt(x: $arg_type, y: $arg_type) -> bool { + x > y + } + #[rhai_fn(name = ">=")] + #[inline(always)] + pub fn gte(x: $arg_type, y: $arg_type) -> bool { + x >= y + } + #[rhai_fn(name = "==")] + #[inline(always)] + pub fn eq(x: $arg_type, y: $arg_type) -> bool { + x == y + } + #[rhai_fn(name = "!=")] + #[inline(always)] + pub fn ne(x: $arg_type, y: $arg_type) -> bool { + x != y } } - )* } + })* } }; } diff --git a/src/packages/math_basic.rs b/src/packages/math_basic.rs index f1de6c4f..a7c17e50 100644 --- a/src/packages/math_basic.rs +++ b/src/packages/math_basic.rs @@ -26,17 +26,15 @@ pub const MAX_INT: INT = i64::MAX; macro_rules! gen_conversion_functions { ($root:ident => $func_name:ident ( $($arg_type:ident),+ ) -> $result_type:ty) => { - pub mod $root { $( - pub mod $arg_type { - use super::super::*; + pub mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_fn] - #[inline(always)] - pub fn $func_name(x: $arg_type) -> $result_type { - x as $result_type - } + #[export_fn] + #[inline(always)] + pub fn $func_name(x: $arg_type) -> $result_type { + x as $result_type } - )* } + })* } } } diff --git a/src/packages/string_basic.rs b/src/packages/string_basic.rs index ae5a84e0..d6f57c02 100644 --- a/src/packages/string_basic.rs +++ b/src/packages/string_basic.rs @@ -22,17 +22,15 @@ type Unit = (); macro_rules! gen_functions { ($root:ident => $fn_name:ident ( $($arg_type:ident),+ )) => { - pub mod $root { $( - pub mod $arg_type { - use super::super::*; + pub mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_fn] - #[inline(always)] - pub fn to_string_func(x: &mut $arg_type) -> ImmutableString { - super::super::$fn_name(x) - } + #[export_fn] + #[inline(always)] + pub fn to_string_func(x: &mut $arg_type) -> ImmutableString { + super::super::$fn_name(x) } - )* } + })* } } } @@ -143,7 +141,7 @@ fn print_string(s: ImmutableString) -> ImmutableString { #[export_fn] #[inline(always)] fn debug_fn_ptr(f: &mut FnPtr) -> ImmutableString { - f.to_string().into() + to_string(f) } #[inline(always)] fn to_string(x: &mut T) -> ImmutableString { diff --git a/src/packages/string_more.rs b/src/packages/string_more.rs index 883d4fec..3766d487 100644 --- a/src/packages/string_more.rs +++ b/src/packages/string_more.rs @@ -12,28 +12,26 @@ use crate::utils::StaticVec; use crate::{result::EvalAltResult, token::Position}; use crate::stdlib::{ - any::TypeId, boxed::Box, fmt::Display, format, mem, string::String, string::ToString, vec::Vec, + any::TypeId, boxed::Box, format, mem, string::String, string::ToString, vec::Vec, }; macro_rules! gen_concat_functions { ($root:ident => $($arg_type:ident),+ ) => { - pub mod $root { $( - pub mod $arg_type { - use super::super::*; + pub mod $root { $(pub mod $arg_type { + use super::super::*; - #[export_fn] - #[inline(always)] - pub fn append_func(x: &mut ImmutableString, y: $arg_type) -> String { - super::super::add_append(x, y) - } - - #[export_fn] - #[inline(always)] - pub fn prepend_func(x: &mut $arg_type, y: ImmutableString) -> String { - super::super::add_prepend(x, y) - } + #[export_fn] + #[inline] + pub fn append_func(x: &mut ImmutableString, y: $arg_type) -> String { + format!("{}{}", x, y) } - )* } + + #[export_fn] + #[inline] + pub fn prepend_func(x: &mut $arg_type, y: ImmutableString) -> String { + format!("{}{}", x, y) + } + })* } } } @@ -116,15 +114,6 @@ def_package!(crate:MoreStringPackage:"Additional string utilities, including str ); }); -#[inline] -fn add_prepend(x: &mut T, y: ImmutableString) -> String { - format!("{}{}", x, y) -} -#[inline] -fn add_append(x: &mut ImmutableString, y: T) -> String { - format!("{}{}", x, y) -} - gen_concat_functions!(basic => INT, bool, char, FnPtr); #[cfg(not(feature = "only_i32"))]