From fd07a4b4e03f1fa7296d57df91cd59eba89e50f4 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 16:27:51 +0800 Subject: [PATCH 01/12] Bump version. --- Cargo.toml | 2 +- doc/src/context.json | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index b785865d..95e54ab9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,7 +6,7 @@ members = [ [package] name = "rhai" -version = "0.19.8" +version = "0.19.9" edition = "2018" authors = ["Jonathan Turner", "Lukáš Hozda", "Stephen Chung", "jhwgh1968"] description = "Embedded scripting for Rust" diff --git a/doc/src/context.json b/doc/src/context.json index 5a3f0288..52bebcc6 100644 --- a/doc/src/context.json +++ b/doc/src/context.json @@ -1,5 +1,5 @@ { - "version": "0.19.8", + "version": "0.19.9", "repoHome": "https://github.com/jonathandturner/rhai/blob/master", "repoTree": "https://github.com/jonathandturner/rhai/tree/master", "rootUrl": "", From 7cd24ec1970153491a06046734e8c267c4c318a0 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 16:28:01 +0800 Subject: [PATCH 02/12] Fix links. --- doc/src/language/doc-comments.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/doc/src/language/doc-comments.md b/doc/src/language/doc-comments.md index 747413d3..a3fdcf91 100644 --- a/doc/src/language/doc-comments.md +++ b/doc/src/language/doc-comments.md @@ -1,6 +1,8 @@ Doc-Comments ============ +{{#include ../links.md}} + Similar to Rust, comments starting with `///` (three slashes) or `/**` (two asterisks) are _doc-comments_. @@ -56,7 +58,6 @@ This is consistent with popular comment block styles for C-like languages. ``` - Using Doc-Comments ------------------ From 2e7ec8f1db8fcb334011f5a112278bd69bce4b3d Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 16:45:56 +0800 Subject: [PATCH 03/12] IdentX -> Ident. --- src/ast.rs | 56 +++------------- src/engine.rs | 34 +++++----- src/lib.rs | 2 +- src/module/mod.rs | 12 ++-- src/parser.rs | 155 ++++++++++++++++++++++++++------------------- tests/optimizer.rs | 2 +- 6 files changed, 123 insertions(+), 138 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 5b0262a1..24aa32e1 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -640,25 +640,6 @@ impl AsRef for AST { } } -/// _(INTERNALS)_ An identifier containing a [string][String] name and a [position][Position]. -/// Exported under the `internals` feature only. -/// -/// ## WARNING -/// -/// This type is volatile and may change. -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct Ident { - pub name: String, - pub pos: Position, -} - -impl Ident { - /// Create a new `Identifier`. - pub fn new(name: String, pos: Position) -> Self { - Self { name, pos } - } -} - /// _(INTERNALS)_ An identifier containing an [immutable string][ImmutableString] name and a [position][Position]. /// Exported under the `internals` feature only. /// @@ -666,30 +647,11 @@ impl Ident { /// /// This type is volatile and may change. #[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct IdentX { +pub struct Ident { pub name: ImmutableString, pub pos: Position, } -impl From for IdentX { - fn from(value: Ident) -> Self { - Self { - name: value.name.into(), - pos: value.pos, - } - } -} - -impl IdentX { - /// Create a new `Identifier`. - pub fn new(name: impl Into, pos: Position) -> Self { - Self { - name: name.into(), - pos, - } - } -} - /// _(INTERNALS)_ A type encapsulating the mode of a `return`/`throw` statement. /// Exported under the `internals` feature only. /// @@ -729,9 +691,9 @@ pub enum Stmt { /// `for` id `in` expr `{` stmt `}` For(Expr, Box<(String, Stmt)>, Position), /// \[`export`\] `let` id `=` expr - Let(Box, Option, bool, Position), + Let(Box, Option, bool, Position), /// \[`export`\] `const` id `=` expr - Const(Box, Option, bool, Position), + Const(Box, Option, bool, Position), /// expr op`=` expr Assignment(Box<(Expr, Cow<'static, str>, Expr)>, Position), /// `{` stmt`;` ... `}` @@ -748,13 +710,13 @@ pub enum Stmt { Return((ReturnType, Position), Option, Position), /// `import` expr `as` var #[cfg(not(feature = "no_module"))] - Import(Expr, Option>, Position), + Import(Expr, Option>, Position), /// `export` var `as` var `,` ... #[cfg(not(feature = "no_module"))] - Export(Vec<(IdentX, Option)>, Position), + Export(Vec<(Ident, Option)>, Position), /// Convert a variable to shared. #[cfg(not(feature = "no_closure"))] - Share(IdentX), + Share(Ident), } impl Default for Stmt { @@ -996,13 +958,13 @@ pub enum Expr { /// [ expr, ... ] Array(Box>, Position), /// #{ name:expr, ... } - Map(Box>, Position), + Map(Box>, Position), /// () Unit(Position), /// Variable access - (optional index, optional modules, hash, variable name) - Variable(Box<(Option, Option>, u64, IdentX)>), + Variable(Box<(Option, Option>, u64, Ident)>), /// Property access - (getter, setter), prop - Property(Box<((ImmutableString, ImmutableString), IdentX)>), + Property(Box<((ImmutableString, ImmutableString), Ident)>), /// { [statement][Stmt] } Stmt(Box>, Position), /// Wrapped [expression][`Expr`] - should not be optimized away. diff --git a/src/engine.rs b/src/engine.rs index 9e578753..af54b2da 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -1,6 +1,6 @@ //! Main module defining the script evaluation [`Engine`]. -use crate::ast::{Expr, FnCallExpr, Ident, IdentX, ReturnType, Stmt}; +use crate::ast::{Expr, FnCallExpr, Ident, ReturnType, Stmt}; use crate::dynamic::{map_std_type_name, AccessMode, Union, Variant}; use crate::fn_call::run_builtin_op_assignment; use crate::fn_native::{ @@ -734,7 +734,7 @@ pub fn search_imports( state: &mut State, namespace: &NamespaceRef, ) -> Result, Box> { - let IdentX { name: root, pos } = &namespace[0]; + let Ident { name: root, pos } = &namespace[0]; // Qualified - check if the root module is directly indexed let index = if state.always_search { @@ -884,7 +884,7 @@ impl Engine { match expr { Expr::Variable(v) => match v.as_ref() { // Qualified variable - (_, Some(modules), hash_var, IdentX { name, pos }) => { + (_, Some(modules), hash_var, Ident { name, pos }) => { let module = search_imports(mods, state, modules)?; let target = module.get_qualified_var(*hash_var).map_err(|mut err| { match *err { @@ -918,7 +918,7 @@ impl Engine { this_ptr: &'s mut Option<&mut Dynamic>, expr: &'a Expr, ) -> Result<(Target<'s>, &'a str, Position), Box> { - let (index, _, _, IdentX { name, pos }) = match expr { + let (index, _, _, Ident { name, pos }) = match expr { Expr::Variable(v) => v.as_ref(), _ => unreachable!(), }; @@ -1115,7 +1115,7 @@ impl Engine { Expr::FnCall(_, _) => unreachable!(), // {xxx:map}.id = ??? Expr::Property(x) if target_val.is::() && new_val.is_some() => { - let IdentX { name, pos } = &x.1; + let Ident { name, pos } = &x.1; let index = name.clone().into(); let mut val = self.get_indexed_mut( mods, state, lib, target_val, index, *pos, true, is_ref, false, level, @@ -1128,7 +1128,7 @@ impl Engine { } // {xxx:map}.id Expr::Property(x) if target_val.is::() => { - let IdentX { name, pos } = &x.1; + let Ident { name, pos } = &x.1; let index = name.clone().into(); let val = self.get_indexed_mut( mods, state, lib, target_val, index, *pos, false, is_ref, false, level, @@ -1138,7 +1138,7 @@ impl Engine { } // xxx.id = ??? Expr::Property(x) if new_val.is_some() => { - let ((_, setter), IdentX { pos, .. }) = x.as_ref(); + let ((_, setter), Ident { pos, .. }) = x.as_ref(); let mut new_val = new_val; let mut args = [target_val, &mut new_val.as_mut().unwrap().0]; self.exec_fn_call( @@ -1150,7 +1150,7 @@ impl Engine { } // xxx.id Expr::Property(x) => { - let ((getter, _), IdentX { pos, .. }) = x.as_ref(); + let ((getter, _), Ident { pos, .. }) = x.as_ref(); let mut args = [target_val]; self.exec_fn_call( mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos, @@ -1163,7 +1163,7 @@ impl Engine { Expr::Index(x, x_pos) | Expr::Dot(x, x_pos) if target_val.is::() => { let mut val = match &x.lhs { Expr::Property(p) => { - let IdentX { name, pos } = &p.1; + let Ident { name, pos } = &p.1; let index = name.clone().into(); self.get_indexed_mut( mods, state, lib, target_val, index, *pos, false, is_ref, true, @@ -1204,7 +1204,7 @@ impl Engine { match &x.lhs { // xxx.prop[expr] | xxx.prop.expr Expr::Property(p) => { - let ((getter, setter), IdentX { pos, .. }) = p.as_ref(); + let ((getter, setter), Ident { pos, .. }) = p.as_ref(); let arg_values = &mut [target_val, &mut Default::default()]; let args = &mut arg_values[..1]; let (mut val, updated) = self @@ -1319,7 +1319,7 @@ impl Engine { match lhs { // id.??? or id[???] Expr::Variable(x) => { - let IdentX { + let Ident { name: var_name, pos: var_pos, } = &x.3; @@ -1675,7 +1675,7 @@ impl Engine { if target.is::() { // map.prop - point directly to the item - let (_, IdentX { name, pos }) = p.as_ref(); + let (_, Ident { name, pos }) = p.as_ref(); let idx = name.clone().into(); if target.is_shared() || target.is_value() { @@ -1693,7 +1693,7 @@ impl Engine { .map(|v| (v, *pos)) } else { // var.prop - call property getter - let ((getter, _), IdentX { pos, .. }) = p.as_ref(); + let ((getter, _), Ident { pos, .. }) = p.as_ref(); let mut args = [target.as_mut()]; self.exec_fn_call( mods, state, lib, getter, 0, &mut args, is_ref, true, false, *pos, @@ -1781,7 +1781,7 @@ impl Engine { Expr::Map(x, _) => { let mut map = Map::with_capacity(crate::stdlib::cmp::max(TYPICAL_MAP_SIZE, x.len())); - for (IdentX { name: key, .. }, expr) in x.as_ref() { + for (Ident { name: key, .. }, expr) in x.as_ref() { map.insert( key.clone(), self.eval_expr(scope, mods, state, lib, this_ptr, expr, level)?, @@ -2272,9 +2272,9 @@ impl Engine { if let Some(Ident { name, .. }) = var_def { let var_name: Cow<'_, str> = if state.is_global() { - name.clone().into() + name.to_string().into() } else { - unsafe_cast_var_name_to_lifetime(name).into() + unsafe_cast_var_name_to_lifetime(&name).into() }; scope.push(var_name, value); } @@ -2410,7 +2410,7 @@ impl Engine { // Export statement #[cfg(not(feature = "no_module"))] Stmt::Export(list, _) => { - for (IdentX { name, pos: id_pos }, rename) in list.iter() { + for (Ident { name, pos: id_pos }, rename) in list.iter() { // Mark scope variables as public if let Some(index) = scope.get_index(name).map(|(i, _)| i) { let alias = rename.as_ref().map(|x| &x.name).unwrap_or_else(|| name); diff --git a/src/lib.rs b/src/lib.rs index f19553d9..ab0b5c1c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -180,7 +180,7 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] pub use ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, + BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, Ident, ReturnType, ScriptFnDef, Stmt, }; #[cfg(feature = "internals")] diff --git a/src/module/mod.rs b/src/module/mod.rs index e5d34f9a..84862afe 100644 --- a/src/module/mod.rs +++ b/src/module/mod.rs @@ -1,6 +1,6 @@ //! Module defining external-loaded modules for Rhai. -use crate::ast::{FnAccess, IdentX}; +use crate::ast::{FnAccess, Ident}; use crate::dynamic::Variant; use crate::fn_native::{shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, SendSync}; use crate::fn_register::by_value as cast_arg; @@ -1906,7 +1906,7 @@ impl Module { /// /// This type is volatile and may change. #[derive(Clone, Eq, PartialEq, Default, Hash)] -pub struct NamespaceRef(StaticVec, Option); +pub struct NamespaceRef(StaticVec, Option); impl fmt::Debug for NamespaceRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { @@ -1921,7 +1921,7 @@ impl fmt::Debug for NamespaceRef { } impl Deref for NamespaceRef { - type Target = StaticVec; + type Target = StaticVec; fn deref(&self) -> &Self::Target { &self.0 @@ -1936,15 +1936,15 @@ impl DerefMut for NamespaceRef { impl fmt::Display for NamespaceRef { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - for IdentX { name, .. } in self.0.iter() { + for Ident { name, .. } in self.0.iter() { write!(f, "{}{}", name, Token::DoubleColon.syntax())?; } Ok(()) } } -impl From> for NamespaceRef { - fn from(modules: StaticVec) -> Self { +impl From> for NamespaceRef { + fn from(modules: StaticVec) -> Self { Self(modules, None) } } diff --git a/src/parser.rs b/src/parser.rs index a8f72932..9a879083 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -1,8 +1,6 @@ //! Main module defining the lexer and parser. -use crate::ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, IdentX, ReturnType, ScriptFnDef, Stmt, -}; +use crate::ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt}; use crate::dynamic::{AccessMode, Union}; use crate::engine::KEYWORD_THIS; use crate::module::NamespaceRef; @@ -703,7 +701,7 @@ fn parse_map_literal( #[cfg(not(feature = "unchecked"))] settings.ensure_level_within_max_limit(state.max_expr_depth)?; - let mut map: StaticVec<(IdentX, Expr)> = Default::default(); + let mut map: StaticVec<(Ident, Expr)> = Default::default(); loop { const MISSING_RBRACE: &str = "to end this object map literal"; @@ -773,7 +771,8 @@ fn parse_map_literal( } let expr = parse_expr(input, state, lib, settings.level_up())?; - map.push((IdentX::new(name, pos), expr)); + let name = state.get_interned_string(name); + map.push((Ident { name, pos }, expr)); match input.peek().unwrap() { (Token::Comma, _) => { @@ -970,7 +969,10 @@ fn parse_primary( { state.allow_capture = true; } - let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); + let var_name_def = Ident { + name: state.get_interned_string(s), + pos: settings.pos, + }; Expr::Variable(Box::new((None, None, 0, var_name_def))) } // Namespace qualification @@ -981,20 +983,29 @@ fn parse_primary( { state.allow_capture = true; } - let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); + let var_name_def = Ident { + name: state.get_interned_string(s), + pos: settings.pos, + }; Expr::Variable(Box::new((None, None, 0, var_name_def))) } // Normal variable access Token::Identifier(s) => { let index = state.access_var(&s, settings.pos); - let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); + let var_name_def = Ident { + name: state.get_interned_string(s), + pos: settings.pos, + }; Expr::Variable(Box::new((index, None, 0, var_name_def))) } // Function call is allowed to have reserved keyword Token::Reserved(s) if *next_token == Token::LeftParen || *next_token == Token::Bang => { if is_keyword_function(&s) { - let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); + let var_name_def = Ident { + name: state.get_interned_string(s), + pos: settings.pos, + }; Expr::Variable(Box::new((None, None, 0, var_name_def))) } else { return Err(PERR::Reserved(s).into_err(settings.pos)); @@ -1007,7 +1018,10 @@ fn parse_primary( let msg = format!("'{}' can only be used in functions", s); return Err(LexError::ImproperSymbol(s, msg).into_err(settings.pos)); } else { - let var_name_def = IdentX::new(state.get_interned_string(s), settings.pos); + let var_name_def = Ident { + name: state.get_interned_string(s), + pos: settings.pos, + }; Expr::Variable(Box::new((None, None, 0, var_name_def))) } } @@ -1067,13 +1081,13 @@ fn parse_primary( .into_err(pos)); } - let (_, modules, _, IdentX { name, pos }) = *x; + let (_, modules, _, Ident { name, pos }) = *x; settings.pos = pos; parse_fn_call(input, state, lib, name, true, modules, settings.level_up())? } // Function call (Expr::Variable(x), Token::LeftParen) => { - let (_, modules, _, IdentX { name, pos }) = *x; + let (_, modules, _, Ident { name, pos }) = *x; settings.pos = pos; parse_fn_call(input, state, lib, name, false, modules, settings.level_up())? } @@ -1091,7 +1105,10 @@ fn parse_primary( modules = Some(Box::new(m)); } - let var_name_def = IdentX::new(state.get_interned_string(id2), pos2); + let var_name_def = Ident { + name: state.get_interned_string(id2), + pos: pos2, + }; Expr::Variable(Box::new((index, modules, 0, var_name_def))) } (Token::Reserved(id2), pos2) if is_valid_identifier(id2.chars()) => { @@ -1129,7 +1146,7 @@ fn parse_primary( _ => None, } .map(|x| { - let (_, modules, hash, IdentX { name, .. }) = x.as_mut(); + let (_, modules, hash, Ident { name, .. }) = x.as_mut(); let namespace = modules.as_mut().unwrap(); // Qualifiers + variable name @@ -1311,58 +1328,46 @@ fn make_assignment_stmt<'a>( state: &mut ParseState, lhs: Expr, rhs: Expr, - pos: Position, + op_pos: Position, ) -> Result { match &lhs { // var (non-indexed) = rhs - Expr::Variable(x) if x.0.is_none() => { - Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) - } + Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment( + Box::new((lhs, fn_name.into(), rhs)), + op_pos, + )), // var (indexed) = rhs Expr::Variable(x) => { - let ( - index, - _, - _, - IdentX { - name, - pos: name_pos, - }, - ) = x.as_ref(); + let (index, _, _, Ident { name, pos }) = x.as_ref(); match state.stack[(state.stack.len() - index.unwrap().get())].1 { - AccessMode::ReadWrite => { - Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) - } + AccessMode::ReadWrite => Ok(Stmt::Assignment( + Box::new((lhs, fn_name.into(), rhs)), + op_pos, + )), // Constant values cannot be assigned to AccessMode::ReadOnly => { - Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos)) + Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos)) } } } // xxx[???] = rhs, xxx.??? = rhs Expr::Index(x, _) | Expr::Dot(x, _) => match &x.lhs { // var[???] (non-indexed) = rhs, var.??? (non-indexed) = rhs - Expr::Variable(x) if x.0.is_none() => { - Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) - } + Expr::Variable(x) if x.0.is_none() => Ok(Stmt::Assignment( + Box::new((lhs, fn_name.into(), rhs)), + op_pos, + )), // var[???] (indexed) = rhs, var.??? (indexed) = rhs Expr::Variable(x) => { - let ( - index, - _, - _, - IdentX { - name, - pos: name_pos, - }, - ) = x.as_ref(); + let (index, _, _, Ident { name, pos }) = x.as_ref(); match state.stack[(state.stack.len() - index.unwrap().get())].1 { - AccessMode::ReadWrite => { - Ok(Stmt::Assignment(Box::new((lhs, fn_name.into(), rhs)), pos)) - } + AccessMode::ReadWrite => Ok(Stmt::Assignment( + Box::new((lhs, fn_name.into(), rhs)), + op_pos, + )), // Constant values cannot be assigned to AccessMode::ReadOnly => { - Err(PERR::AssignmentToConstant(name.to_string()).into_err(*name_pos)) + Err(PERR::AssignmentToConstant(name.to_string()).into_err(*pos)) } } } @@ -1378,7 +1383,7 @@ fn make_assignment_stmt<'a>( "=".to_string(), "Possibly a typo of '=='?".to_string(), ) - .into_err(pos)), + .into_err(op_pos)), // expr = rhs _ => Err(PERR::AssignmentToInvalidLHS("".to_string()).into_err(lhs.position())), } @@ -1883,11 +1888,11 @@ fn parse_custom_syntax( match required_token.as_str() { MARKER_IDENT => match input.next().unwrap() { (Token::Identifier(s), pos) => { - let ident = state.get_interned_string(s); - let var_name_def = IdentX::new(ident.clone(), pos); - keywords.push(Expr::Variable(Box::new((None, None, 0, var_name_def)))); - segments.push(ident); + let name = state.get_interned_string(s); + segments.push(name.clone()); tokens.push(state.get_interned_string(MARKER_IDENT)); + let var_name_def = Ident { name, pos }; + keywords.push(Expr::Variable(Box::new((None, None, 0, var_name_def)))); } (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); @@ -2208,16 +2213,16 @@ fn parse_let( match var_type { // let name = expr AccessMode::ReadWrite => { - let var_name = state.get_interned_string(name.clone()); - state.stack.push((var_name, AccessMode::ReadWrite)); - let var_def = IdentX::new(name, pos); + let name = state.get_interned_string(name); + state.stack.push((name.clone(), AccessMode::ReadWrite)); + let var_def = Ident { name, pos }; Ok(Stmt::Let(Box::new(var_def), init_expr, export, token_pos)) } // const name = { expr:constant } AccessMode::ReadOnly => { - let var_name = state.get_interned_string(name.clone()); - state.stack.push((var_name, AccessMode::ReadOnly)); - let var_def = IdentX::new(name, pos); + let name = state.get_interned_string(name); + state.stack.push((name.clone(), AccessMode::ReadOnly)); + let var_def = Ident { name, pos }; Ok(Stmt::Const(Box::new(var_def), init_expr, export, token_pos)) } } @@ -2261,7 +2266,10 @@ fn parse_import( Ok(Stmt::Import( expr, - Some(Box::new(IdentX::new(name, settings.pos))), + Some(Box::new(Ident { + name, + pos: settings.pos, + })), token_pos, )) } @@ -2310,7 +2318,10 @@ fn parse_export( let rename = if match_token(input, Token::As).0 { match input.next().unwrap() { - (Token::Identifier(s), pos) => Some(IdentX::new(state.get_interned_string(s), pos)), + (Token::Identifier(s), pos) => Some(Ident { + name: state.get_interned_string(s), + pos, + }), (Token::Reserved(s), pos) if is_valid_identifier(s.chars()) => { return Err(PERR::Reserved(s).into_err(pos)); } @@ -2321,7 +2332,13 @@ fn parse_export( None }; - exports.push((IdentX::new(state.get_interned_string(id), id_pos), rename)); + exports.push(( + Ident { + name: state.get_interned_string(id), + pos: id_pos, + }, + rename, + )); match input.peek().unwrap() { (Token::Comma, _) => { @@ -2651,7 +2668,10 @@ fn parse_try_catch( // try { body } catch ( let var_def = if match_token(input, Token::LeftParen).0 { let id = match input.next().unwrap() { - (Token::Identifier(s), pos) => Ident::new(s, pos), + (Token::Identifier(s), pos) => Ident { + name: state.get_interned_string(s), + pos, + }, (_, pos) => return Err(PERR::VariableExpected.into_err(pos)), }; @@ -2780,7 +2800,7 @@ fn parse_fn( /// Creates a curried expression from a list of external variables #[cfg(not(feature = "no_function"))] -fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Position) -> Expr { +fn make_curry_from_externals(fn_expr: Expr, externals: StaticVec, pos: Position) -> Expr { if externals.is_empty() { return fn_expr; } @@ -2889,13 +2909,16 @@ fn parse_anon_fn( // External variables may need to be processed in a consistent order, // so extract them into a list. - let externals: StaticVec = { + let externals: StaticVec = { #[cfg(not(feature = "no_closure"))] { state .externals .iter() - .map(|(k, &v)| IdentX::new(k.clone(), v)) + .map(|(name, &pos)| Ident { + name: name.clone(), + pos, + }) .collect() } #[cfg(feature = "no_closure")] @@ -3114,7 +3137,7 @@ pub fn map_dynamic_to_expr(value: Dynamic, pos: Position) -> Option { Union::Map(map, _) => { let items: Vec<_> = map .into_iter() - .map(|(k, v)| (IdentX::new(k, pos), map_dynamic_to_expr(v, pos))) + .map(|(name, value)| (Ident { name, pos }, map_dynamic_to_expr(value, pos))) .collect(); if items.iter().all(|(_, expr)| expr.is_some()) { diff --git a/tests/optimizer.rs b/tests/optimizer.rs index b6468d03..bfb9c4e0 100644 --- a/tests/optimizer.rs +++ b/tests/optimizer.rs @@ -55,7 +55,7 @@ fn test_optimizer_parse() -> Result<(), Box> { let ast = engine.compile("{ const DECISION = false; if DECISION { 42 } else { 123 } }")?; - assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(IdentX { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#)); + assert!(format!("{:?}", ast).starts_with(r#"AST { source: None, statements: [Block([Const(Ident { name: "DECISION", pos: 1:9 }, Some(Unit(0:0)), false, 1:3), Expr(IntegerConstant(123, 1:53))], 1:1)]"#)); let ast = engine.compile("if 1 == 2 { 42 }")?; From 2a7e6c08842d7c07724632a078e673bc6003ef4c Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 22:35:25 +0800 Subject: [PATCH 04/12] Fixup docs. --- RELEASES.md | 13 +++++++++---- doc/src/engine/metadata/export_to_json.md | 13 +++++++------ src/ast.rs | 2 ++ 3 files changed, 18 insertions(+), 10 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index bbdbc1ba..cd9974cb 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -1,6 +1,10 @@ Rhai Release Notes ================== +Version 0.19.9 +============== + + Version 0.19.8 ============== @@ -14,13 +18,15 @@ A new API, `Engine::gen_fn_metadata_to_json` and `Engine::gen_fn_metadata_with_a paired with the new `metadata` feature, exports the full list of functions metadata (including those in an `AST`) as a JSON document. +There are also a sizable number of bug fixes. + Bug fixes --------- * Unary prefix operators `-`, `+` and `!` now bind correctly when applied to an expression. Previously, `-x.len` is parsed as `(-x).len` which is obviously counter-intuitive. * Indexing of namespace-qualified variables now work properly, such as `path::to::var[x]`. * Constants are no longer propagated by the optimizer if shadowed by a non-constant variable. -* Constants passed as the `this` parameter to Rhai functions now throws an error if assigned to. +* A constant passed as the `this` parameter to Rhai functions now throws an error if assigned to. * Generic type parameter of `Engine::register_iterator` is `IntoIterator` instead of `Iterator`. * Fixes parsing of block comments ending with `**/` or inner blocks starting with `//*`. @@ -37,8 +43,8 @@ New features * `AST::iter_functions` now returns `ScriptFnMetadata` which includes, among others, _doc-comments_ for functions prefixed by `///` or `/**`. * _Doc-comments_ can be enabled/disabled with the new `Engine::set_doc_comments` method. -* A new feature `metadata` is added that pulls in `serde_json` and enables `Engine::gen_fn_metadata_to_json` and ``Engine::gen_fn_metadata_with_ast_to_json` which exports the full list of functions metadata (including those inside an `AST`) in JSON format. -* `Engine::on_debug` provides two additional parameters: `source: Option<&str>` and `pos: Position`. +* A new feature `metadata` is added that pulls in `serde_json` and enables `Engine::gen_fn_metadata_to_json` and `Engine::gen_fn_metadata_with_ast_to_json` which exports the full list of functions metadata (including those inside an `AST`) in JSON format. +* `Engine::on_debug` provides two additional parameters: `source: Option<&str>` and `pos: Position`, containing the current source (if any) and position of the `debug` statement. * `NativeCallContext` and `EvalContext` both expose `source()` which returns the current source, if any. Enhancements @@ -46,7 +52,6 @@ Enhancements * A functions lookup cache is added to make function call resolution faster. * Capturing a constant variable in a closure is now supported, with no cloning. -* Provides position info for `debug` statements. * A _look-ahead_ symbol is provided to custom syntax parsers, which can be used to parse variable-length symbol streams. diff --git a/doc/src/engine/metadata/export_to_json.md b/doc/src/engine/metadata/export_to_json.md index c6476df3..add09c16 100644 --- a/doc/src/engine/metadata/export_to_json.md +++ b/doc/src/engine/metadata/export_to_json.md @@ -4,20 +4,21 @@ Export Functions Metadata to JSON {{#include ../../links.md}} -`Engine::gen_fn_metadata_to_json` --------------------------------- +`Engine::gen_fn_metadata_to_json`
`Engine::gen_fn_metadata_with_ast_to_json` +------------------------------------------------------------------------------ -As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` exports the full list -of [functions metadata] in JSON format. +As part of a _reflections_ API, `Engine::gen_fn_metadata_to_json` and the corresponding +`Engine::gen_fn_metadata_with_ast_to_json` export the full list of [functions metadata] +in JSON format. -The [`metadata`] feature must be used to turn on this method, which requires +The [`metadata`] feature must be used to turn on this API, which requires the [`serde_json`](https://crates.io/crates/serde_json) crate. ### Sources Functions from the following sources are included: -1) Script-defined functions in an [`AST`], if provided +1) Script-defined functions in an [`AST`] (for `Engine::gen_fn_metadata_with_ast_to_json`) 2) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API 3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules registered via [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) diff --git a/src/ast.rs b/src/ast.rs index 24aa32e1..9a207461 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -648,7 +648,9 @@ impl AsRef for AST { /// This type is volatile and may change. #[derive(Debug, Clone, Eq, PartialEq, Hash)] pub struct Ident { + /// Identifier name. pub name: ImmutableString, + /// Declaration position. pub pos: Position, } From 0157f912e069c90102f8b435336a864451817b26 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 22:36:30 +0800 Subject: [PATCH 05/12] Remove PackageLibrary and PackagesCollection. --- src/engine_api.rs | 2 +- src/engine_settings.rs | 7 ++--- src/fn_call.rs | 13 +++++--- src/packages/mod.rs | 70 ++---------------------------------------- 4 files changed, 16 insertions(+), 76 deletions(-) diff --git a/src/engine_api.rs b/src/engine_api.rs index 9f72a5df..78b96e6c 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -1681,7 +1681,7 @@ impl Engine { }); if include_packages { - signatures.extend(self.packages.gen_fn_signatures()); + signatures.extend(self.packages.iter().flat_map(|m| m.gen_fn_signatures())); } signatures diff --git a/src/engine_settings.rs b/src/engine_settings.rs index ca220cb1..e61dc08f 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -1,9 +1,8 @@ //! Configuration settings for [`Engine`]. -use crate::packages::PackageLibrary; use crate::stdlib::{format, string::String}; use crate::token::{is_valid_identifier, Token}; -use crate::Engine; +use crate::{Engine, Module, Shared}; #[cfg(not(feature = "no_module"))] use crate::stdlib::boxed::Box; @@ -15,8 +14,8 @@ impl Engine { /// When searching for functions, packages loaded later are preferred. /// In other words, loaded packages are searched in reverse order. #[inline(always)] - pub fn load_package(&mut self, package: impl Into) -> &mut Self { - self.packages.add(package.into()); + pub fn load_package(&mut self, package: impl Into>) -> &mut Self { + self.packages.insert(0, package.into()); self } /// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation. diff --git a/src/fn_call.rs b/src/fn_call.rs index d0b320d2..68701342 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -184,7 +184,7 @@ impl Engine { let f = self .global_namespace .get_fn(hash_fn, pub_only) - .or_else(|| self.packages.get_fn(hash_fn)) + .or_else(|| self.packages.iter().find_map(|m| m.get_fn(hash_fn, false))) .or_else(|| mods.get_fn(hash_fn)); state.functions_cache.insert(hash_fn, f.cloned()); @@ -460,8 +460,8 @@ impl Engine { //|| (hash_script != 0 && self.global_namespace.contains_fn(hash_script, pub_only)) || self.global_namespace.contains_fn(hash_fn, false) // Then check packages - || (hash_script != 0 && self.packages.contains_fn(hash_script)) - || self.packages.contains_fn(hash_fn) + || (hash_script != 0 && self.packages.iter().any(|m| m.contains_fn(hash_script, false))) + || self.packages.iter().any(|m| m.contains_fn(hash_fn, false)) // Then check imported modules || (hash_script != 0 && mods.map(|m| m.contains_fn(hash_script)).unwrap_or(false)) || mods.map(|m| m.contains_fn(hash_fn)).unwrap_or(false) @@ -541,7 +541,12 @@ impl Engine { .map(|f| (f, m.id_raw().clone())) }) //.or_else(|| self.global_namespace.get_fn(hash_script, pub_only)) - .or_else(|| self.packages.get_fn(hash_script).map(|f| (f, None))) + .or_else(|| { + self.packages + .iter() + .find_map(|m| m.get_fn(hash_script, false)) + .map(|f| (f, None)) + }) //.or_else(|| mods.iter().find_map(|(_, m)| m.get_qualified_fn(hash_script).map(|f| (f, m.id_raw().clone())))) .unwrap(); diff --git a/src/packages/mod.rs b/src/packages/mod.rs index ca42cced..e5bffc58 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -39,71 +39,7 @@ pub trait Package { fn init(lib: &mut Module); /// Retrieve the generic package library from this package. - fn get(&self) -> PackageLibrary; -} - -/// A sharable [`Module`][crate::Module] to facilitate sharing library instances. -pub type PackageLibrary = Shared; - -/// Type containing a collection of [`PackageLibrary`] instances. -/// All function and type iterator keys in the loaded packages are indexed for fast access. -#[derive(Debug, Clone, Default)] -pub(crate) struct PackagesCollection(Option>); - -impl PackagesCollection { - /// Add a [`PackageLibrary`] into the [`PackagesCollection`]. - /// - /// Packages are searched in reverse order. - pub fn add(&mut self, package: PackageLibrary) { - if self.0.is_none() { - self.0 = Some(Default::default()); - } - // Later packages override previous ones. - self.0.as_mut().unwrap().insert(0, package); - } - /// Does the specified function hash key exist in the [`PackagesCollection`]? - #[allow(dead_code)] - pub fn contains_fn(&self, hash: u64) -> bool { - if hash == 0 { - false - } else { - self.0 - .as_ref() - .map_or(false, |x| x.iter().any(|p| p.contains_fn(hash, false))) - } - } - /// Get specified function via its hash key. - pub fn get_fn(&self, hash: u64) -> Option<&CallableFunction> { - if hash == 0 { - None - } else { - self.0 - .as_ref() - .and_then(|x| x.iter().find_map(|p| p.get_fn(hash, false))) - } - } - /// Does the specified [`TypeId`] iterator exist in the [`PackagesCollection`]? - #[allow(dead_code)] - pub fn contains_iter(&self, id: TypeId) -> bool { - self.0 - .as_ref() - .map_or(false, |x| x.iter().any(|p| p.contains_iter(id))) - } - /// Get the specified [`TypeId`] iterator. - pub fn get_iter(&self, id: TypeId) -> Option { - self.0 - .as_ref() - .and_then(|x| x.iter().find_map(|p| p.get_iter(id))) - } - /// Get an iterator over all the packages in the [`PackagesCollection`]. - pub(crate) fn iter(&self) -> impl Iterator { - self.0.iter().flat_map(|p| p.iter()) - } - - /// Generate signatures for all the functions in the [`PackagesCollection`]. - pub fn gen_fn_signatures<'a>(&'a self) -> impl Iterator + 'a { - self.iter().flat_map(|m| m.gen_fn_signatures()) - } + fn get(&self) -> Shared; } /// Macro that makes it easy to define a _package_ (which is basically a shared module) @@ -132,10 +68,10 @@ impl PackagesCollection { macro_rules! def_package { ($root:ident : $package:ident : $comment:expr , $lib:ident , $block:stmt) => { #[doc=$comment] - pub struct $package($root::packages::PackageLibrary); + pub struct $package($root::Shared<$root::Module>); impl $root::packages::Package for $package { - fn get(&self) -> $root::packages::PackageLibrary { + fn get(&self) -> $root::Shared<$root::Module> { self.0.clone() } From eb46ec8296adef902bd45a04181518fd26001e3f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 22:36:36 +0800 Subject: [PATCH 06/12] Simplify Imports. --- src/engine.rs | 78 ++++++++++++++++++++------------------------------- 1 file changed, 31 insertions(+), 47 deletions(-) diff --git a/src/engine.rs b/src/engine.rs index af54b2da..90f81283 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -9,7 +9,7 @@ use crate::fn_native::{ }; use crate::module::NamespaceRef; use crate::optimize::OptimizationLevel; -use crate::packages::{Package, PackagesCollection, StandardPackage}; +use crate::packages::{Package, StandardPackage}; use crate::r#unsafe::unsafe_cast_var_name_to_lifetime; use crate::stdlib::{ any::{type_name, TypeId}, @@ -55,74 +55,60 @@ pub const TYPICAL_MAP_SIZE: usize = 8; // Small maps are typical // the module name will live beyond the AST of the eval script text. // The best we can do is a shared reference. #[derive(Debug, Clone, Default)] -pub struct Imports(Option)>>); +pub struct Imports(StaticVec<(ImmutableString, Shared)>); impl Imports { /// Get the length of this stack of imported [modules][Module]. pub fn len(&self) -> usize { - self.0.as_ref().map_or(0, StaticVec::len) + self.0.len() } /// Is this stack of imported [modules][Module] empty? pub fn is_empty(&self) -> bool { - self.0.as_ref().map_or(true, StaticVec::is_empty) + self.0.is_empty() } /// Get the imported [modules][Module] at a particular index. pub fn get(&self, index: usize) -> Option> { - self.0 - .as_ref() - .and_then(|x| x.get(index)) - .map(|(_, m)| m) - .cloned() + self.0.get(index).map(|(_, m)| m).cloned() } /// Get the index of an imported [modules][Module] by name. pub fn find(&self, name: &str) -> Option { - self.0.as_ref().and_then(|x| { - x.iter() - .enumerate() - .rev() - .find(|(_, (key, _))| key.as_str() == name) - .map(|(index, _)| index) - }) + self.0 + .iter() + .enumerate() + .rev() + .find(|(_, (key, _))| key.as_str() == name) + .map(|(index, _)| index) } /// Push an imported [modules][Module] onto the stack. pub fn push(&mut self, name: impl Into, module: impl Into>) { - if self.0.is_none() { - self.0 = Some(Default::default()); - } - - self.0.as_mut().unwrap().push((name.into(), module.into())); + self.0.push((name.into(), module.into())); } /// Truncate the stack of imported [modules][Module] to a particular length. pub fn truncate(&mut self, size: usize) { - if self.0.is_some() { - self.0.as_mut().unwrap().truncate(size); - } + self.0.truncate(size); } /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] pub fn iter<'a>(&'a self) -> impl Iterator + 'a { - self.0.iter().flat_map(|lib| { - lib.iter() - .rev() - .map(|(name, module)| (name.as_str(), module.as_ref())) - }) + self.0 + .iter() + .rev() + .map(|(name, module)| (name.as_str(), module.as_ref())) } /// Get an iterator to this stack of imported [modules][Module] in reverse order. #[allow(dead_code)] pub(crate) fn iter_raw<'a>( &'a self, ) -> impl Iterator)> + 'a { - self.0 - .iter() - .flat_map(|lib| lib.iter().rev().map(|(n, m)| (n, m))) + self.0.iter().rev().map(|(n, m)| (n, m)) } /// Get a consuming iterator to this stack of imported [modules][Module] in reverse order. pub fn into_iter(self) -> impl Iterator)> { - self.0.into_iter().flat_map(|lib| lib.into_iter().rev()) + self.0.into_iter().rev() } /// Add a stream of imported [modules][Module]. pub fn extend(&mut self, stream: impl Iterator)>) { - self.0.as_mut().unwrap().extend(stream) + self.0.extend(stream) } /// Does the specified function hash key exist in this stack of imported [modules][Module]? #[allow(dead_code)] @@ -130,9 +116,7 @@ impl Imports { if hash == 0 { false } else { - self.0.as_ref().map_or(false, |x| { - x.iter().any(|(_, m)| m.contains_qualified_fn(hash)) - }) + self.0.iter().any(|(_, m)| m.contains_qualified_fn(hash)) } } /// Get specified function via its hash key. @@ -141,22 +125,22 @@ impl Imports { None } else { self.0 - .as_ref() - .and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_fn(hash))) + .iter() + .rev() + .find_map(|(_, m)| m.get_qualified_fn(hash)) } } /// Does the specified [`TypeId`][std::any::TypeId] iterator exist in this stack of imported [modules][Module]? #[allow(dead_code)] pub fn contains_iter(&self, id: TypeId) -> bool { - self.0.as_ref().map_or(false, |x| { - x.iter().any(|(_, m)| m.contains_qualified_iter(id)) - }) + self.0.iter().any(|(_, m)| m.contains_qualified_iter(id)) } /// Get the specified [`TypeId`][std::any::TypeId] iterator. pub fn get_iter(&self, id: TypeId) -> Option { self.0 - .as_ref() - .and_then(|x| x.iter().rev().find_map(|(_, m)| m.get_qualified_iter(id))) + .iter() + .rev() + .find_map(|(_, m)| m.get_qualified_iter(id)) } } @@ -630,7 +614,7 @@ pub struct Engine { /// A module containing all functions directly loaded into the Engine. pub(crate) global_namespace: Module, /// A collection of all library packages loaded into the Engine. - pub(crate) packages: PackagesCollection, + pub(crate) packages: StaticVec>, /// A collection of all sub-modules directly loaded into the Engine. pub(crate) global_sub_modules: Imports, @@ -1987,7 +1971,7 @@ impl Engine { match self .global_namespace .get_fn(hash_fn, false) - .or_else(|| self.packages.get_fn(hash_fn)) + .or_else(|| self.packages.iter().find_map(|m| m.get_fn(hash_fn, false))) .or_else(|| mods.get_fn(hash_fn)) { // op= function registered as method @@ -2196,7 +2180,7 @@ impl Engine { let func = self .global_namespace .get_iter(iter_type) - .or_else(|| self.packages.get_iter(iter_type)) + .or_else(|| self.packages.iter().find_map(|m| m.get_iter(iter_type))) .or_else(|| mods.get_iter(iter_type)); if let Some(func) = func { From 15fb03218cdade8d62b90da8d73c20a079c7c62f Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Tue, 22 Dec 2020 23:45:14 +0800 Subject: [PATCH 07/12] Rename packages to global modules. --- RELEASES.md | 11 ++++- benches/engine.rs | 2 +- benches/eval_module.rs | 2 +- codegen/src/lib.rs | 10 ++--- codegen/tests/test_modules.rs | 2 +- doc/src/SUMMARY.md | 3 +- doc/src/engine/metadata/export_to_json.md | 6 +-- doc/src/engine/metadata/gen_fn_sig.md | 6 +-- doc/src/language/fn-namespaces.md | 13 +++--- doc/src/language/functions.md | 2 +- doc/src/language/modules/export.md | 2 +- doc/src/patterns/enums.md | 2 +- doc/src/patterns/events.md | 2 +- doc/src/patterns/parallel.md | 25 ++++++----- doc/src/patterns/singleton.md | 2 +- doc/src/plugins/module.md | 48 ++++++++++---------- doc/src/rust/modules/create.md | 32 +++++++------- doc/src/rust/packages/builtin.md | 3 +- doc/src/rust/packages/create.md | 12 ++--- doc/src/rust/packages/index.md | 53 +++++++---------------- doc/src/rust/packages/plugin.md | 42 ------------------ doc/src/start/builds/minimal.md | 4 +- src/engine.rs | 24 ++++++---- src/engine_api.rs | 34 ++++++++++++--- src/engine_settings.rs | 12 +---- src/fn_call.rs | 12 +++-- src/packages/mod.rs | 8 ++-- src/serde_impl/metadata.rs | 18 ++++---- tests/for.rs | 2 +- tests/functions.rs | 2 +- tests/modules.rs | 2 +- tests/packages.rs | 6 +-- tests/plugins.rs | 4 +- tests/plugins_unroll.rs | 2 +- 34 files changed, 192 insertions(+), 218 deletions(-) delete mode 100644 doc/src/rust/packages/plugin.md diff --git a/RELEASES.md b/RELEASES.md index cd9974cb..79824a05 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,6 +4,13 @@ Rhai Release Notes Version 0.19.9 ============== +Breaking changes +---------------- + +* `Engine::load_package` is renamed `Engine::register_global_module`. +* `Package::get` is renamed `Package::as_shared_module`. +* `Engine::register_module` is renamed `Engine::register_static_module`. + Version 0.19.8 ============== @@ -103,7 +110,7 @@ New features * New `switch` statement. * New `do ... while` and `do ... until` statements. * New `Engine::gen_fn_signatures`, `Module::gen_fn_signatures` and `PackagesCollection::gen_fn_signatures` to generate a list of signatures for functions registered. -* New `Engine::register_module` to register a module as a sub-module in the global namespace. +* New `Engine::register_static_module` to register a module as a sub-module in the global namespace. * New `set_exported_global_fn!` macro to register a plugin function and expose it to the global namespace. * `Module::set_fn_XXX_mut` can expose a module function to the global namespace. This is convenient when registering an API for a custom type. * `Module::set_getter_fn`, `Module::set_setter_fn`, `Module::set_indexer_get_fn`, `Module::set_indexer_set_fn` all expose the function to the global namespace by default. This is convenient when registering an API for a custom type. @@ -370,7 +377,7 @@ Breaking changes * `Engine::register_raw_fn_XXX` API shortcuts are removed. * `PackagesCollection::get_fn`, `PackagesCollection::contains_fn`, `Module::get_fn` and `Module::contains_fn` now take an additional `public_only` parameter indicating whether only public functions are accepted. * The iterator returned by `Scope::iter` now contains a clone of the `Dynamic` value (unshared). -* `Engine::load_package` takes any type that is `Into`. +* `Engine::register_global_module` takes any type that is `Into`. * Error in `Engine::register_custom_syntax` is no longer `Box`-ed. Housekeeping diff --git a/benches/engine.rs b/benches/engine.rs index f1ac588b..0cfc2972 100644 --- a/benches/engine.rs +++ b/benches/engine.rs @@ -23,7 +23,7 @@ fn bench_engine_new_raw_core(bench: &mut Bencher) { bench.iter(|| { let mut engine = Engine::new_raw(); - engine.load_package(package.get()); + engine.register_global_module(package.as_shared_module()); }); } diff --git a/benches/eval_module.rs b/benches/eval_module.rs index ac6f56d4..a3a3920a 100644 --- a/benches/eval_module.rs +++ b/benches/eval_module.rs @@ -20,7 +20,7 @@ fn bench_eval_module(bench: &mut Bencher) { let module = Module::eval_ast_as_new(Default::default(), &ast, &engine).unwrap(); - engine.register_module("testing", module); + engine.register_static_module("testing", module); let ast = engine .compile( diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index 38ee893a..ff96271b 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -160,7 +160,7 @@ pub fn export_fn( /// /// let module = exported_module!(my_plugin_module); /// -/// engine.load_package(module); +/// engine.register_global_module(module); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) @@ -203,7 +203,7 @@ pub fn export_module( /// /// let module = exported_module!(my_plugin_module); /// -/// engine.load_package(module); +/// engine.register_global_module(module); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) @@ -250,7 +250,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke /// let mut module = Module::new(); /// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module); /// -/// engine.load_package(module); +/// engine.register_global_module(module); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) @@ -324,7 +324,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS /// let mut module = Module::new(); /// set_exported_fn!(module, "func", my_plugin_function); /// -/// engine.load_package(module); +/// engine.register_global_module(module); /// /// assert_eq!(engine.eval::("func(21)")?, 42); /// # Ok(()) @@ -366,7 +366,7 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream /// let mut module = Module::new(); /// set_exported_global_fn!(module, "func", my_plugin_function); /// -/// engine.register_module("test", module); +/// engine.register_static_module("test", module); /// /// assert_eq!(engine.eval::("func(21)")?, 42); /// # Ok(()) diff --git a/codegen/tests/test_modules.rs b/codegen/tests/test_modules.rs index 2217b384..7f4fb462 100644 --- a/codegen/tests/test_modules.rs +++ b/codegen/tests/test_modules.rs @@ -257,7 +257,7 @@ mod multiple_fn_rename { fn multiple_fn_rename_test() -> Result<(), Box> { let mut engine = Engine::new(); let m = rhai::exported_module!(crate::multiple_fn_rename::my_adds); - engine.load_package(m); + engine.register_global_module(m); let output_array = engine.eval::( r#" diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index a0a76d3c..4adec297 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -43,8 +43,7 @@ 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. [Load a Plugin Module as a Package](rust/packages/plugin.md) - 3. [Manually Create a Custom Package](rust/packages/create.md) + 2. [Custom Packages](rust/packages/create.md) 10. [Modules](rust/modules/index.md) 1. [Create from Rust](rust/modules/create.md) 2. [Create from AST](rust/modules/ast.md) diff --git a/doc/src/engine/metadata/export_to_json.md b/doc/src/engine/metadata/export_to_json.md index add09c16..13d2da72 100644 --- a/doc/src/engine/metadata/export_to_json.md +++ b/doc/src/engine/metadata/export_to_json.md @@ -20,9 +20,9 @@ Functions from the following sources are included: 1) Script-defined functions in an [`AST`] (for `Engine::gen_fn_metadata_with_ast_to_json`) 2) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API -3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules registered via - [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) -4) Native Rust functions in registered [packages] (optional) +3) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in static modules + registered via `Engine::register_static_module` +4) Native Rust functions in global modules registered via `Engine::register_global_module` (optional) Notice that if a function has been [overloaded][function overloading], only the overriding function's metadata is included. diff --git a/doc/src/engine/metadata/gen_fn_sig.md b/doc/src/engine/metadata/gen_fn_sig.md index ee8390fc..30067c60 100644 --- a/doc/src/engine/metadata/gen_fn_sig.md +++ b/doc/src/engine/metadata/gen_fn_sig.md @@ -17,9 +17,9 @@ As part of a _reflections_ API, `Engine::gen_fn_signatures` returns a list of fu Functions from the following sources are included, in order: 1) Native Rust functions registered into the global namespace via the `Engine::register_XXX` API -2) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules registered via - [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) -3) Native Rust functions in registered [packages] (optional) +2) _Public_ (i.e. non-[`private`]) functions (native Rust or Rhai scripted) in global sub-modules + registered via `Engine::register_static_module`. +3) Native Rust functions in global modules registered via `Engine::register_global_module` (optional) Functions Metadata diff --git a/doc/src/language/fn-namespaces.md b/doc/src/language/fn-namespaces.md index 61111f65..ef4cbd77 100644 --- a/doc/src/language/fn-namespaces.md +++ b/doc/src/language/fn-namespaces.md @@ -15,10 +15,10 @@ allow combining all functions in one [`AST`] into another, forming a new, unifie In general, there are two types of _namespaces_ where functions are looked up: -| Namespace | How Many | Source | Lookup method | Sub-modules? | Variables? | -| --------- | :------: | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: | -| Global | One | 1) [`AST`] being evaluated
2) `Engine::register_XXX` API
3) [packages] loaded
4) functions in [modules] loaded via `Engine::register_module` and marked _global_ | simple function name | ignored | ignored | -| Module | Many | [`Module`] | namespace-qualified function name | yes | yes | +| Namespace | How Many | Source | Lookup method | Sub-modules? | Variables? | +| --------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: | +| Global | One | 1) [`AST`] being evaluated
2) `Engine::register_XXX` API
3) global [modules] loaded via `Engine::register_global_module`
4) functions in static [modules] loaded via `Engine::register_static_module` and marked _global_ | simple function name | ignored | ignored | +| Module | Many | [`Module`] | namespace-qualified function name | yes | yes | Module Namespace @@ -52,9 +52,10 @@ There is one _global_ namespace for every [`Engine`], which includes (in the fol * All native Rust functions and iterators registered via the `Engine::register_XXX` API. -* All functions and iterators defined in [packages] that are loaded into the [`Engine`]. +* All functions and iterators defined in global [modules] that are registered into the [`Engine`] via + `Engine::register_global_module`. -* Functions defined in [modules] loaded via `Engine::register_module` that are specifically marked +* Functions defined in [modules] loaded via `Engine::register_static_module` that are specifically marked for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]). Anywhere in a Rhai script, when a function call is made, the function is searched within the diff --git a/doc/src/language/functions.md b/doc/src/language/functions.md index 677deebc..1a896346 100644 --- a/doc/src/language/functions.md +++ b/doc/src/language/functions.md @@ -172,7 +172,7 @@ Functions from the following sources are returned, in order: 1) Encapsulated script environment (e.g. when loading a [module] from a script file), 2) Current script, 3) [Modules] imported via the [`import`] statement (latest imports first), -4) [Modules] added via [`Engine::register_module`]({{rootUrl}}/rust/modules/create.md) (latest registrations first) +4) [Modules] added via [`Engine::register_static_module`]({{rootUrl}}/rust/modules/create.md) (latest registrations first) The return value is an [array] of [object maps] (so `get_fn_metadata_list` is not available under [`no_index`] or [`no_object`]), containing the following fields: diff --git a/doc/src/language/modules/export.md b/doc/src/language/modules/export.md index 6ff7ea3b..362ca111 100644 --- a/doc/src/language/modules/export.md +++ b/doc/src/language/modules/export.md @@ -4,7 +4,7 @@ Export Variables, Functions and Sub-Modules in Module {{#include ../../links.md}} -The easiest way to expose a package of functions as a self-contained [module] is to do it via a Rhai script itself. +The easiest way to expose a collection of functions as a self-contained [module] is to do it via a Rhai script itself. See the section on [_Creating a Module from AST_]({{rootUrl}}/rust/modules/ast.md) for more details. diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md index c061bab9..6adeb6d4 100644 --- a/doc/src/patterns/enums.md +++ b/doc/src/patterns/enums.md @@ -116,7 +116,7 @@ let mut engine = Engine::new(); // Load the module as the module namespace "MyEnum" engine .register_type_with_name::("MyEnum") - .register_module("MyEnum", exported_module!(MyEnumModule)); + .register_static_module("MyEnum", exported_module!(MyEnumModule)); ``` With this API in place, working with enums feels almost the same as in Rust: diff --git a/doc/src/patterns/events.md b/doc/src/patterns/events.md index e7c5d946..24574612 100644 --- a/doc/src/patterns/events.md +++ b/doc/src/patterns/events.md @@ -100,7 +100,7 @@ impl Handler { // Register custom types and API's engine .register_type_with_name::("SomeType") - .load_package(exported_module!(SomeTypeAPI)); + .register_global_module(exported_module!(SomeTypeAPI)); // Create a custom 'Scope' to hold state let mut scope = Scope::new(); diff --git a/doc/src/patterns/parallel.md b/doc/src/patterns/parallel.md index e070b78d..3db89b3f 100644 --- a/doc/src/patterns/parallel.md +++ b/doc/src/patterns/parallel.md @@ -20,7 +20,8 @@ Usage Scenario Key Concepts ------------ -* Create a single instance of each standard [package] required. To duplicate `Engine::new`, create a [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md). +* Create a single instance of each standard [package] required. + To duplicate `Engine::new`, create a [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md). * Gather up all common custom functions into a [custom package]. @@ -29,10 +30,14 @@ Key Concepts * Always use `Engine::new_raw` to create a [raw `Engine`], instead of `Engine::new` which is _much_ more expensive. A [raw `Engine`] is _extremely_ cheap to create. - Loading the [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md) into a [raw `Engine`] via `Engine::load_package` is essentially the same as `Engine::new`. - But because packages are shared, loading an existing package is _much cheaper_ than registering all the functions one by one. + Registering the [`StandardPackage`]({{rootUrl}}/rust/packages/builtin.md) into a [raw `Engine`] via + `Engine::register_global_module` is essentially the same as `Engine::new`. + + However, because packages are shared, using existing package is _much cheaper_ than + registering all the functions one by one. -* Load the required packages into the [raw `Engine`] via `Engine::load_package`, using `Package::get` to obtain a shared copy. +* Register the required packages with the [raw `Engine`] via `Engine::register_global_module`, + using `Package::as_shared_module` to obtain a shared [module]. Examples @@ -46,17 +51,17 @@ let std_pkg = StandardPackage::new(); let custom_pkg = MyCustomPackage::new(); let make_call = |x: i64| -> Result<(), Box> { - // Create a raw Engine - extremely cheap. + // Create a raw Engine - extremely cheap let mut engine = Engine::new_raw(); - // Load packages - cheap. - engine.load_package(std_pkg.get()); - engine.load_package(custom_pkg.get()); + // Register packages as global modules - cheap + engine.register_global_module(std_pkg.as_shared_module()); + engine.register_global_module(custom_pkg.as_shared_module()); - // Create custom scope - cheap. + // Create custom scope - cheap let mut scope = Scope::new(); - // Push variable into scope - relatively cheap. + // Push variable into scope - relatively cheap scope.push("x", x); // Evaluate script. diff --git a/doc/src/patterns/singleton.md b/doc/src/patterns/singleton.md index 6f59f70a..316ab302 100644 --- a/doc/src/patterns/singleton.md +++ b/doc/src/patterns/singleton.md @@ -146,7 +146,7 @@ pub mod bunny_api { } } -engine.load_package(exported_module!(bunny_api)); +engine.register_global_module(exported_module!(bunny_api)); ``` ### Push Constant Command Object into Custom Scope diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index a8343220..a6b3b8af 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -28,11 +28,11 @@ and is custom fit to each exported item. All `pub` functions become registered functions, all `pub` constants become [module] constant variables, and all sub-modules become Rhai sub-modules. -This Rust module can then either be loaded into an [`Engine`] as a normal [module] or -registered as a [package]. This is done by using the `exported_module!` macro. +This Rust module can then be registered into an [`Engine`] as a normal [module]. +This is done via the `exported_module!` macro. -The macro `combine_with_exported_module!` can also be used to _combine_ all the functions -and variables into an existing module, _flattening_ the namespace - i.e. all sub-modules +The macro `combine_with_exported_module!` can be used to _combine_ all the functions +and variables into an existing [module], _flattening_ the namespace - i.e. all sub-modules are eliminated and their contents promoted to the top level. This is typical for developing [custom packages]. @@ -42,7 +42,7 @@ use rhai::plugin::*; // a "prelude" import for macros #[export_module] mod my_module { // This constant will be registered as the constant variable 'MY_NUMBER'. - // Ignored when loaded as a package. + // Ignored when registered as a global module. pub const MY_NUMBER: i64 = 42; // This function will be registered as 'greet'. @@ -64,9 +64,9 @@ mod my_module { 42 } - // Sub-modules are ignored when the Module is loaded as a package. + // Sub-modules are ignored when the module is registered globally. pub mod my_sub_module { - // This function is ignored when loaded as a package. + // This function is ignored when registered globally. // Otherwise it is a valid registered function under a sub-module. pub fn get_info() -> String { "hello".to_string() @@ -78,7 +78,7 @@ mod my_module { // This is currently a limitation of the plugin procedural macros. #[cfg(feature = "advanced_functions")] pub mod advanced { - // This function is ignored when loaded as a package. + // This function is ignored when registered globally. // Otherwise it is a valid registered function under a sub-module // which only exists when the 'advanced_functions' feature is used. pub fn advanced_calc(input: i64) -> i64 { @@ -88,10 +88,10 @@ mod my_module { } ``` -### Use `Engine::load_package` +### Use `Engine::register_global_module` -The simplest way to load this into an [`Engine`] is to first use the `exported_module!` macro -to turn it into a normal Rhai [module], then use the `Engine::load_package` method on it: +The simplest way to register this into an [`Engine`] is to first use the `exported_module!` macro +to turn it into a normal Rhai [module], then use the `Engine::register_global_module` method on it: ```rust fn main() { @@ -100,13 +100,13 @@ fn main() { // The macro call creates a Rhai module from the plugin module. let module = exported_module!(my_module); - // A module can simply be loaded, registering all public functions. - engine.load_package(module); + // A module can simply be registered into the global namespace. + engine.register_global_module(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. +are automatically registered into the [`Engine`] when `Engine::register_global_module` is called. ```rust let x = greet("world"); @@ -123,12 +123,14 @@ x == 43; ``` 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. +can be accessed. -### Use `Engine::register_module` +Variables as well as sub-modules are **ignored**. -Another simple way to load this into an [`Engine`] is, again, to use the `exported_module!` macro -to turn it into a normal Rhai [module], then use the `Engine::register_module` method on it: +### Use `Engine::register_static_module` + +Another simple way to register this into an [`Engine`] is, again, to use the `exported_module!` macro +to turn it into a normal Rhai [module], then use the `Engine::register_static_module` method on it: ```rust fn main() { @@ -137,13 +139,13 @@ fn main() { // The macro call creates a Rhai module from the plugin module. let module = exported_module!(my_module); - // A module can simply be loaded as a globally-available module. - engine.register_module("service", module); + // A module can simply be registered as a static module namespace. + engine.register_static_module("service", module); } ``` The functions contained within the module definition (i.e. `greet`, `get_num` and `increment`), -plus the constant `MY_NUMBER`, are automatically loaded under the module namespace `service`: +plus the constant `MY_NUMBER`, are automatically registered under the module namespace `service`: ```rust let x = service::greet("world"); @@ -178,14 +180,14 @@ x.increment(); x == 43; ``` -### Use as loadable `Module` +### Use Dynamically Using this directly as a dynamically-loadable 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. -### Use as custom package +### Combine into Custom Package Finally the plugin module can also be used to develop a [custom package], using `combine_with_exported_module!`: diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index 4c279800..d0af3a55 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -19,14 +19,16 @@ 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` Globally Available +----------------------------------- -`Engine::load_package` supports loading a [module] as a [package]. +`Engine::register_global_module` registers a shared [module] into the _global_ namespace. -Since it acts as a [package], all functions will be registered into the _global_ namespace -and can be accessed without _namespace qualifiers_. This is by far the easiest way to expose -a module's functionalities to Rhai. +All [functions] and [type iterators] can be accessed without _namespace qualifiers_. + +Variables and sub-modules are **ignored**. + +This is by far the easiest way to expose a module's functionalities to Rhai. ```rust use rhai::{Engine, Module}; @@ -40,18 +42,18 @@ let hash = module.set_fn_1("inc", |x: i64| Ok(x+1)); // 'set_fn_XXX' by default does not set function metadata. module.update_fn_metadata(hash, ["x: i64", "i64"]); -// Load the module into the Engine as a new package. +// Register the module into the global namespace of the Engine. let mut engine = Engine::new(); -engine.load_package(module); +engine.register_global_module(module); engine.eval::("inc(41)")? == 42; // no need to import module ``` -Make the `Module` a Global Module ------------------------------------- +Make the `Module` a Static Module +-------------------------------- -`Engine::register_module` loads a [module] and makes it available globally under a specific namespace. +`Engine::register_static_module` registers a [module] and under a specific module namespace. ```rust use rhai::{Engine, Module}; @@ -65,9 +67,9 @@ let hash = module.set_fn_1("inc", |x: i64| Ok(x+1)); // 'set_fn_XXX' by default does not set function metadata. module.update_fn_metadata(hash, ["x: i64", "i64"]); -// Load the module into the Engine as a sub-module named 'calc' +// Register the module into the Engine as a static module namespace 'calc' let mut engine = Engine::new(); -engine.register_module("calc", module); +engine.register_static_module("calc", module); engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module ``` @@ -89,9 +91,9 @@ let hash = module.set_fn_1_mut("inc", FnNamespace::Global, |x: &mut i64| Ok(x+1) // 'set_fn_XXX' by default does not set function metadata. module.update_fn_metadata(hash, ["x: &mut i64", "i64"]); -// Load the module into the Engine as a sub-module named 'calc' +// Register the module into the Engine as a static module namespace 'calc' let mut engine = Engine::new(); -engine.register_module("calc", module); +engine.register_static_module("calc", module); // The method 'inc' works as expected because it is exposed to the global namespace engine.eval::("let x = 41; x.inc()")? == 42; diff --git a/doc/src/rust/packages/builtin.md b/doc/src/rust/packages/builtin.md index a430e17e..5d49ca0a 100644 --- a/doc/src/rust/packages/builtin.md +++ b/doc/src/rust/packages/builtin.md @@ -35,5 +35,6 @@ use rhai::packages::{Package, CorePackage}; let mut engine = Engine::new_raw(); let package = CorePackage::new(); -engine.load_package(package.get()); +// Register the package into the Engine by converting it into a shared module. +engine.register_global_module(package.as_shared_module()); ``` diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index ec573804..9b3f654a 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -1,17 +1,17 @@ -Manually Create a Custom Package -=============================== +Create a Custom Package +======================= {{#include ../../links.md}} Sometimes specific functionalities are needed, so custom packages can be created. A custom package is a convenient means to gather up a number of functions for later use. -An [`Engine`] only needs to `Engine::load_package` the custom package once to gain access +An [`Engine`] only needs to `Engine::register_global_module` the custom package once to gain access to the entire set of functions within. -Loading a package into an [`Engine`] is functionally equivalent to calling `Engine::register_fn` etc. -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. +Registering a package into an [`Engine`] is functionally equivalent to calling `Engine::register_fn` etc. +on _each_ of the functions inside the package. But because packages are _shared_, using a package is +_much_ cheaper than registering all the functions one by one. The macro `rhai::def_package!` can be used to create a new custom package. diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index fe6fc36f..541e6f49 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -3,16 +3,17 @@ Packages {{#include ../../links.md}} -Standard built-in Rhai features are provided in various _packages_ that can be loaded via a call to `Engine::load_package`. +Standard built-in Rhai features are provided in various _packages_ that can be registered into the +_global namespace_ of an [`Engine`] via `Engine::register_global_module`. Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` must be loaded in order for 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_ +All _top-level_ functions in a package are available under the _global namespace_ (i.e. they're available without namespace qualifiers). -Once a package is created (e.g. via `Package::new`), it can be _shared_ (via `Package::get`) +Once a package is created (e.g. via `Package::new`), it can be _shared_ (via `Package::as_shared_module`) among multiple instances of [`Engine`], even across threads (under [`sync`]). Therefore, a package only has to be created _once_. @@ -21,43 +22,21 @@ use rhai::Engine; use rhai::packages::Package // load the 'Package' trait to use packages use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic) -let mut engine = Engine::new_raw(); // create a 'raw' Engine -let package = CorePackage::new(); // create a package - can be shared among multiple `Engine` instances +// Create a 'raw' Engine +let mut engine = Engine::new_raw(); -engine.load_package(package.get()); // load the package manually. 'get' returns a reference to the shared package +// Create a package - can be shared among multiple `Engine` instances +let package = CorePackage::new(); + +// Register the package into the global namespace. +// 'Package::as_shared_module' converts the package into a shared module. +engine.register_global_module(package.as_shared_module()); ``` -Difference Between a Package and a Module ----------------------------------------- +Share a Package Among `Engine`s +------------------------------ -Packages are internally implemented as [modules], so they share a lot of behavior and characteristics. +`Engine::register_global_module` consumes the input shared module. -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]). - -A package is _static_ (i.e. pre-loaded into an [`Engine`]), while a module is _dynamic_ (i.e. loaded with -the `import` statement). - -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. - - -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`]. +However, `Package::as_shared_module` can be called multiple times for multiple instances of [`Engine`]. diff --git a/doc/src/rust/packages/plugin.md b/doc/src/rust/packages/plugin.md deleted file mode 100644 index 509aff32..00000000 --- a/doc/src/rust/packages/plugin.md +++ /dev/null @@ -1,42 +0,0 @@ -Load a Plugin Module as a Package -================================ - -{{#include ../../links.md}} - -[Plugin modules] can be loaded as a package just like a normal [module] -via the `exported_module!` macro. - -```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/doc/src/start/builds/minimal.md b/doc/src/start/builds/minimal.md index 71ed7bd9..45882382 100644 --- a/doc/src/start/builds/minimal.md +++ b/doc/src/start/builds/minimal.md @@ -49,6 +49,6 @@ Use a Raw [`Engine`] A _raw_ engine supports, out of the box, only a very [restricted set]({{rootUrl}}/engine/raw.md#built-in-operators) of basic arithmetic and logical operators. -Selectively include other necessary functionalities by loading specific [packages] to minimize the footprint. +Selectively include other necessary functionalities by picking specific [packages] to minimize the footprint. -Packages are sharable (even across threads via the [`sync`] feature), so they only have to be created once. +Packages are shared (even across threads via the [`sync`] feature), so they only have to be created once. diff --git a/src/engine.rs b/src/engine.rs index 90f81283..bb6cb872 100644 --- a/src/engine.rs +++ b/src/engine.rs @@ -613,8 +613,8 @@ pub struct Engine { /// A module containing all functions directly loaded into the Engine. pub(crate) global_namespace: Module, - /// A collection of all library packages loaded into the Engine. - pub(crate) packages: StaticVec>, + /// A collection of all modules loaded into the global namespace of the Engine. + pub(crate) global_modules: StaticVec>, /// A collection of all sub-modules directly loaded into the Engine. pub(crate) global_sub_modules: Imports, @@ -745,8 +745,8 @@ impl Engine { let mut engine = Self { id: Default::default(), - packages: Default::default(), global_namespace: Default::default(), + global_modules: Default::default(), global_sub_modules: Default::default(), #[cfg(not(feature = "no_module"))] @@ -798,20 +798,20 @@ impl Engine { disable_doc_comments: false, }; - engine.load_package(StandardPackage::new().get()); + engine.register_global_module(StandardPackage::new().as_shared_module()); engine } /// Create a new [`Engine`] with minimal built-in functions. - /// Use the [`load_package`][Engine::load_package] method to load additional packages of functions. + /// Use the [`register_global_module`][Engine::register_global_module] method to load additional packages of functions. #[inline] pub fn new_raw() -> Self { Self { id: Default::default(), - packages: Default::default(), global_namespace: Default::default(), + global_modules: Default::default(), global_sub_modules: Default::default(), #[cfg(not(feature = "no_module"))] @@ -1971,7 +1971,11 @@ impl Engine { match self .global_namespace .get_fn(hash_fn, false) - .or_else(|| self.packages.iter().find_map(|m| m.get_fn(hash_fn, false))) + .or_else(|| { + self.global_modules + .iter() + .find_map(|m| m.get_fn(hash_fn, false)) + }) .or_else(|| mods.get_fn(hash_fn)) { // op= function registered as method @@ -2180,7 +2184,11 @@ impl Engine { let func = self .global_namespace .get_iter(iter_type) - .or_else(|| self.packages.iter().find_map(|m| m.get_iter(iter_type))) + .or_else(|| { + self.global_modules + .iter() + .find_map(|m| m.get_iter(iter_type)) + }) .or_else(|| mods.get_iter(iter_type)); if let Some(func) = func { diff --git a/src/engine_api.rs b/src/engine_api.rs index 78b96e6c..4506601b 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -14,8 +14,8 @@ use crate::stdlib::{ }; use crate::utils::get_hasher; use crate::{ - scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, NativeCallContext, - ParseError, Position, AST, + scope::Scope, Dynamic, Engine, EvalAltResult, FnAccess, FnNamespace, Module, NativeCallContext, + ParseError, Position, Shared, AST, }; #[cfg(not(feature = "no_index"))] @@ -723,7 +723,23 @@ impl Engine { self.register_indexer_get(getter) .register_indexer_set(setter) } - /// Register a [`Module`][crate::Module] as a fixed module namespace with the [`Engine`]. + /// Register a shared [`Module`][crate::Module] into the global namespace of [`Engine`]. + /// + /// All functions and type iterators are automatically available to scripts without namespace qualifications. + /// + /// Sub-modules and variables are **ignored**. + /// + /// When searching for functions, modules loaded later are preferred. + /// In other words, loaded modules are searched in reverse order. + #[inline(always)] + pub fn register_global_module(&mut self, package: impl Into>) -> &mut Self { + // Insert the module into the front + self.global_modules.insert(0, package.into()); + self + } + /// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`]. + /// + /// Functions marked `FnNamespace::Global` and type iterators are exposed to scripts without namespace qualifications. /// /// # Example /// @@ -738,17 +754,17 @@ impl Engine { /// module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// /// // Register the module as a fixed sub-module - /// engine.register_module("CalcService", module); + /// engine.register_static_module("CalcService", module); /// /// assert_eq!(engine.eval::("CalcService::calc(41)")?, 42); /// # Ok(()) /// # } /// ``` #[cfg(not(feature = "no_module"))] - pub fn register_module( + pub fn register_static_module( &mut self, name: impl Into, - module: impl Into>, + module: impl Into>, ) -> &mut Self { let module = module.into(); @@ -1681,7 +1697,11 @@ impl Engine { }); if include_packages { - signatures.extend(self.packages.iter().flat_map(|m| m.gen_fn_signatures())); + signatures.extend( + self.global_modules + .iter() + .flat_map(|m| m.gen_fn_signatures()), + ); } signatures diff --git a/src/engine_settings.rs b/src/engine_settings.rs index e61dc08f..7d5d17f4 100644 --- a/src/engine_settings.rs +++ b/src/engine_settings.rs @@ -2,22 +2,12 @@ use crate::stdlib::{format, string::String}; use crate::token::{is_valid_identifier, Token}; -use crate::{Engine, Module, Shared}; +use crate::Engine; #[cfg(not(feature = "no_module"))] use crate::stdlib::boxed::Box; impl Engine { - /// Load a new package into the [`Engine`]. - /// A simple [`Module`][crate::Module] is automatically converted into a package. - /// - /// When searching for functions, packages loaded later are preferred. - /// In other words, loaded packages are searched in reverse order. - #[inline(always)] - pub fn load_package(&mut self, package: impl Into>) -> &mut Self { - self.packages.insert(0, package.into()); - self - } /// Control whether and how the [`Engine`] will optimize an [`AST`][crate::AST] after compilation. /// /// Not available under the `no_optimize` feature. diff --git a/src/fn_call.rs b/src/fn_call.rs index 68701342..6d19919d 100644 --- a/src/fn_call.rs +++ b/src/fn_call.rs @@ -184,7 +184,11 @@ impl Engine { let f = self .global_namespace .get_fn(hash_fn, pub_only) - .or_else(|| self.packages.iter().find_map(|m| m.get_fn(hash_fn, false))) + .or_else(|| { + self.global_modules + .iter() + .find_map(|m| m.get_fn(hash_fn, false)) + }) .or_else(|| mods.get_fn(hash_fn)); state.functions_cache.insert(hash_fn, f.cloned()); @@ -460,8 +464,8 @@ impl Engine { //|| (hash_script != 0 && self.global_namespace.contains_fn(hash_script, pub_only)) || self.global_namespace.contains_fn(hash_fn, false) // Then check packages - || (hash_script != 0 && self.packages.iter().any(|m| m.contains_fn(hash_script, false))) - || self.packages.iter().any(|m| m.contains_fn(hash_fn, false)) + || (hash_script != 0 && self.global_modules.iter().any(|m| m.contains_fn(hash_script, false))) + || self.global_modules.iter().any(|m| m.contains_fn(hash_fn, false)) // Then check imported modules || (hash_script != 0 && mods.map(|m| m.contains_fn(hash_script)).unwrap_or(false)) || mods.map(|m| m.contains_fn(hash_fn)).unwrap_or(false) @@ -542,7 +546,7 @@ impl Engine { }) //.or_else(|| self.global_namespace.get_fn(hash_script, pub_only)) .or_else(|| { - self.packages + self.global_modules .iter() .find_map(|m| m.get_fn(hash_script, false)) .map(|f| (f, None)) diff --git a/src/packages/mod.rs b/src/packages/mod.rs index e5bffc58..89bb7fbb 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -1,8 +1,6 @@ //! Module containing all built-in _packages_ available to Rhai, plus facilities to define custom packages. -use crate::fn_native::{CallableFunction, IteratorFn}; -use crate::stdlib::{any::TypeId, string::String}; -use crate::{Module, Shared, StaticVec}; +use crate::{Module, Shared}; pub(crate) mod arithmetic; mod array_basic; @@ -39,7 +37,7 @@ pub trait Package { fn init(lib: &mut Module); /// Retrieve the generic package library from this package. - fn get(&self) -> Shared; + fn as_shared_module(&self) -> Shared; } /// Macro that makes it easy to define a _package_ (which is basically a shared module) @@ -71,7 +69,7 @@ macro_rules! def_package { pub struct $package($root::Shared<$root::Module>); impl $root::packages::Package for $package { - fn get(&self) -> $root::Shared<$root::Module> { + fn as_shared_module(&self) -> $root::Shared<$root::Module> { self.0.clone() } diff --git a/src/serde_impl/metadata.rs b/src/serde_impl/metadata.rs index da0e6ab1..b8bb010b 100644 --- a/src/serde_impl/metadata.rs +++ b/src/serde_impl/metadata.rs @@ -219,17 +219,17 @@ impl Engine { /// Functions from the following sources are included: /// 1) Functions defined in an [`AST`][crate::AST] /// 2) Functions registered into the global namespace - /// 3) Functions in registered sub-modules - /// 4) Functions in packages (optional) + /// 3) Functions in static modules + /// 4) Functions in global modules (optional) pub fn gen_fn_metadata_with_ast_to_json( &self, ast: &AST, - include_packages: bool, + include_global: bool, ) -> serde_json::Result { let mut global: ModuleMetadata = Default::default(); - if include_packages { - self.packages + if include_global { + self.global_modules .iter() .flat_map(|m| m.iter_fn().map(|f| f.into())) .for_each(|info| global.functions.push(info)); @@ -260,9 +260,9 @@ impl Engine { /// /// Functions from the following sources are included: /// 1) Functions registered into the global namespace - /// 2) Functions in registered sub-modules - /// 3) Functions in packages (optional) - pub fn gen_fn_metadata_to_json(&self, include_packages: bool) -> serde_json::Result { - self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_packages) + /// 2) Functions in static modules + /// 3) Functions in global modules (optional) + pub fn gen_fn_metadata_to_json(&self, include_global: bool) -> serde_json::Result { + self.gen_fn_metadata_with_ast_to_json(&Default::default(), include_global) } } diff --git a/tests/for.rs b/tests/for.rs index 56899fd1..fea8e56e 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -101,7 +101,7 @@ fn test_for_module_iterator() -> Result<(), Box> { let mut module = Module::new(); module.set_sub_module("inner", sub_module); - engine.register_module("testing", module); + engine.register_static_module("testing", module); let script = r#" let item = testing::inner::new_ts(); diff --git a/tests/functions.rs b/tests/functions.rs index 2034095f..e8e10cec 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -62,7 +62,7 @@ fn test_functions_namespaces() -> Result<(), Box> { let hash = m.set_fn_0("test", || Ok(999 as INT)); m.update_fn_namespace(hash, FnNamespace::Global); - engine.register_module("hello", m); + engine.register_static_module("hello", m); assert_eq!(engine.eval::("test()")?, 999); assert_eq!(engine.eval::("fn test() { 123 } test()")?, 123); diff --git a/tests/modules.rs b/tests/modules.rs index 656dc728..fb799343 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -44,7 +44,7 @@ fn test_module_sub_module() -> Result<(), Box> { assert_eq!(m2.get_var_value::("answer").unwrap(), 41); let mut engine = Engine::new(); - engine.register_module("question", module); + engine.register_static_module("question", module); assert_eq!(engine.eval::("question::MYSTIC_NUMBER")?, 42); assert!(engine.eval::("MYSTIC_NUMBER").is_err()); diff --git a/tests/packages.rs b/tests/packages.rs index ae31e719..8729c373 100644 --- a/tests/packages.rs +++ b/tests/packages.rs @@ -11,8 +11,8 @@ fn test_packages() -> Result<(), Box> { // Create a raw Engine - extremely cheap. let mut engine = Engine::new_raw(); - // Load packages - cheap. - engine.load_package(std_pkg.get()); + // Register packages - cheap. + engine.register_global_module(std_pkg.as_shared_module()); // Create custom scope - cheap. let mut scope = Scope::new(); @@ -37,7 +37,7 @@ fn test_packages_with_script() -> Result<(), Box> { let ast = engine.compile("fn foo(x) { x + 1 } fn bar(x) { foo(x) + 1 }")?; let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?; - engine.load_package(module); + engine.register_global_module(module); assert_eq!(engine.eval::("foo(41)")?, 42); assert_eq!(engine.eval::("bar(40)")?, 42); diff --git a/tests/plugins.rs b/tests/plugins.rs index e3e8d1a1..ed9d5d07 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -77,7 +77,7 @@ fn test_plugins_package() -> Result<(), Box> { let mut m = Module::new(); combine_with_exported_module!(&mut m, "test", test::special_array_package); - engine.load_package(m); + engine.register_global_module(m); reg_functions!(engine += greet::single(INT, bool, char)); @@ -95,7 +95,7 @@ fn test_plugins_package() -> Result<(), Box> { "6 kitties" ); - engine.register_module("test", exported_module!(test::special_array_package)); + engine.register_static_module("test", exported_module!(test::special_array_package)); assert_eq!(engine.eval::("test::MYSTIC_NUMBER")?, 42); diff --git a/tests/plugins_unroll.rs b/tests/plugins_unroll.rs index c872381a..533f2de8 100644 --- a/tests/plugins_unroll.rs +++ b/tests/plugins_unroll.rs @@ -52,7 +52,7 @@ fn test_generated_ops() -> Result<(), Box> { register_in_bulk!(m, add, i8, i16, i32, i64); register_in_bulk!(m, mul, i8, i16, i32, i64); - engine.load_package(m); + engine.register_global_module(m); #[cfg(feature = "only_i32")] assert_eq!(engine.eval::("let a = 0; add_i32(a, 1)")?, 1); From f55a56c6bce362746574a53904f9ec0ed7a8b0cd Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Dec 2020 10:08:43 +0800 Subject: [PATCH 08/12] Engine::register_global_module and Engine::register_static_module must be passed shared Module. --- RELEASES.md | 7 +++++-- benches/eval_module.rs | 2 +- codegen/src/lib.rs | 10 +++++----- codegen/tests/test_modules.rs | 2 +- src/engine_api.rs | 10 ++++------ tests/for.rs | 2 +- tests/functions.rs | 2 +- tests/modules.rs | 2 +- tests/packages.rs | 2 +- tests/plugins.rs | 4 ++-- tests/plugins_unroll.rs | 2 +- 11 files changed, 23 insertions(+), 22 deletions(-) diff --git a/RELEASES.md b/RELEASES.md index 79824a05..2e9a14e5 100644 --- a/RELEASES.md +++ b/RELEASES.md @@ -4,12 +4,15 @@ Rhai Release Notes Version 0.19.9 ============== +This version removes the confusing differences between _packages_ and _modules_ +by unifying the terminology and API under the global umbrella of _modules_. + Breaking changes ---------------- -* `Engine::load_package` is renamed `Engine::register_global_module`. +* `Engine::load_package` is renamed `Engine::register_global_module` and now must explicitly pass a shared [`Module`]. +* `Engine::register_module` is renamed `Engine::register_static_module` and now must explicitly pass a shared [`Module`]. * `Package::get` is renamed `Package::as_shared_module`. -* `Engine::register_module` is renamed `Engine::register_static_module`. Version 0.19.8 diff --git a/benches/eval_module.rs b/benches/eval_module.rs index a3a3920a..9b053bcd 100644 --- a/benches/eval_module.rs +++ b/benches/eval_module.rs @@ -20,7 +20,7 @@ fn bench_eval_module(bench: &mut Bencher) { let module = Module::eval_ast_as_new(Default::default(), &ast, &engine).unwrap(); - engine.register_static_module("testing", module); + engine.register_static_module("testing", module.into()); let ast = engine .compile( diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index ff96271b..c4d50c1f 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -160,7 +160,7 @@ pub fn export_fn( /// /// let module = exported_module!(my_plugin_module); /// -/// engine.register_global_module(module); +/// engine.register_global_module(module.into); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) @@ -203,7 +203,7 @@ pub fn export_module( /// /// let module = exported_module!(my_plugin_module); /// -/// engine.register_global_module(module); +/// engine.register_global_module(module.into()); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) @@ -250,7 +250,7 @@ pub fn exported_module(module_path: proc_macro::TokenStream) -> proc_macro::Toke /// let mut module = Module::new(); /// combine_with_exported_module!(&mut module, "my_plugin_module_ID", my_plugin_module); /// -/// engine.register_global_module(module); +/// engine.register_global_module(module.into()); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) @@ -324,7 +324,7 @@ pub fn register_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenS /// let mut module = Module::new(); /// set_exported_fn!(module, "func", my_plugin_function); /// -/// engine.register_global_module(module); +/// engine.register_global_module(module.into()); /// /// assert_eq!(engine.eval::("func(21)")?, 42); /// # Ok(()) @@ -366,7 +366,7 @@ pub fn set_exported_fn(args: proc_macro::TokenStream) -> proc_macro::TokenStream /// let mut module = Module::new(); /// set_exported_global_fn!(module, "func", my_plugin_function); /// -/// engine.register_static_module("test", module); +/// engine.register_static_module("test", module.into()); /// /// assert_eq!(engine.eval::("func(21)")?, 42); /// # Ok(()) diff --git a/codegen/tests/test_modules.rs b/codegen/tests/test_modules.rs index 7f4fb462..878f4fef 100644 --- a/codegen/tests/test_modules.rs +++ b/codegen/tests/test_modules.rs @@ -257,7 +257,7 @@ mod multiple_fn_rename { fn multiple_fn_rename_test() -> Result<(), Box> { let mut engine = Engine::new(); let m = rhai::exported_module!(crate::multiple_fn_rename::my_adds); - engine.register_global_module(m); + engine.register_global_module(m.into()); let output_array = engine.eval::( r#" diff --git a/src/engine_api.rs b/src/engine_api.rs index 4506601b..78164e29 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -732,9 +732,9 @@ impl Engine { /// When searching for functions, modules loaded later are preferred. /// In other words, loaded modules are searched in reverse order. #[inline(always)] - pub fn register_global_module(&mut self, package: impl Into>) -> &mut Self { + pub fn register_global_module(&mut self, module: Shared) -> &mut Self { // Insert the module into the front - self.global_modules.insert(0, package.into()); + self.global_modules.insert(0, module); self } /// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`]. @@ -754,7 +754,7 @@ impl Engine { /// module.set_fn_1("calc", |x: i64| Ok(x + 1)); /// /// // Register the module as a fixed sub-module - /// engine.register_static_module("CalcService", module); + /// engine.register_static_module("CalcService", module.into()); /// /// assert_eq!(engine.eval::("CalcService::calc(41)")?, 42); /// # Ok(()) @@ -764,10 +764,8 @@ impl Engine { pub fn register_static_module( &mut self, name: impl Into, - module: impl Into>, + module: Shared, ) -> &mut Self { - let module = module.into(); - if !module.is_indexed() { // Index the module (making a clone copy if necessary) if it is not indexed let mut module = crate::fn_native::shared_take_or_clone(module); diff --git a/tests/for.rs b/tests/for.rs index fea8e56e..03a40a89 100644 --- a/tests/for.rs +++ b/tests/for.rs @@ -101,7 +101,7 @@ fn test_for_module_iterator() -> Result<(), Box> { let mut module = Module::new(); module.set_sub_module("inner", sub_module); - engine.register_static_module("testing", module); + engine.register_static_module("testing", module.into()); let script = r#" let item = testing::inner::new_ts(); diff --git a/tests/functions.rs b/tests/functions.rs index e8e10cec..68b8a214 100644 --- a/tests/functions.rs +++ b/tests/functions.rs @@ -62,7 +62,7 @@ fn test_functions_namespaces() -> Result<(), Box> { let hash = m.set_fn_0("test", || Ok(999 as INT)); m.update_fn_namespace(hash, FnNamespace::Global); - engine.register_static_module("hello", m); + engine.register_static_module("hello", m.into()); assert_eq!(engine.eval::("test()")?, 999); assert_eq!(engine.eval::("fn test() { 123 } test()")?, 123); diff --git a/tests/modules.rs b/tests/modules.rs index fb799343..b07d4d5d 100644 --- a/tests/modules.rs +++ b/tests/modules.rs @@ -44,7 +44,7 @@ fn test_module_sub_module() -> Result<(), Box> { assert_eq!(m2.get_var_value::("answer").unwrap(), 41); let mut engine = Engine::new(); - engine.register_static_module("question", module); + engine.register_static_module("question", module.into()); assert_eq!(engine.eval::("question::MYSTIC_NUMBER")?, 42); assert!(engine.eval::("MYSTIC_NUMBER").is_err()); diff --git a/tests/packages.rs b/tests/packages.rs index 8729c373..528eaf1d 100644 --- a/tests/packages.rs +++ b/tests/packages.rs @@ -37,7 +37,7 @@ fn test_packages_with_script() -> Result<(), Box> { let ast = engine.compile("fn foo(x) { x + 1 } fn bar(x) { foo(x) + 1 }")?; let module = Module::eval_ast_as_new(Scope::new(), &ast, &engine)?; - engine.register_global_module(module); + engine.register_global_module(module.into()); assert_eq!(engine.eval::("foo(41)")?, 42); assert_eq!(engine.eval::("bar(40)")?, 42); diff --git a/tests/plugins.rs b/tests/plugins.rs index ed9d5d07..4ba79f6f 100644 --- a/tests/plugins.rs +++ b/tests/plugins.rs @@ -77,7 +77,7 @@ fn test_plugins_package() -> Result<(), Box> { let mut m = Module::new(); combine_with_exported_module!(&mut m, "test", test::special_array_package); - engine.register_global_module(m); + engine.register_global_module(m.into()); reg_functions!(engine += greet::single(INT, bool, char)); @@ -95,7 +95,7 @@ fn test_plugins_package() -> Result<(), Box> { "6 kitties" ); - engine.register_static_module("test", exported_module!(test::special_array_package)); + engine.register_static_module("test", exported_module!(test::special_array_package).into()); assert_eq!(engine.eval::("test::MYSTIC_NUMBER")?, 42); diff --git a/tests/plugins_unroll.rs b/tests/plugins_unroll.rs index 533f2de8..9003e530 100644 --- a/tests/plugins_unroll.rs +++ b/tests/plugins_unroll.rs @@ -52,7 +52,7 @@ fn test_generated_ops() -> Result<(), Box> { register_in_bulk!(m, add, i8, i16, i32, i64); register_in_bulk!(m, mul, i8, i16, i32, i64); - engine.register_global_module(m); + engine.register_global_module(m.into()); #[cfg(feature = "only_i32")] assert_eq!(engine.eval::("let a = 0; add_i32(a, 1)")?, 1); From cebbc2b607dcdbd1de6f83be5d93ddc06c4d68ac Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Dec 2020 12:11:53 +0800 Subject: [PATCH 09/12] Fix duplicate Ident export. --- src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ab0b5c1c..946fda30 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -179,9 +179,7 @@ pub use token::{get_next_token, parse_string_literal, InputStream, Token, Tokeni #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] -pub use ast::{ - BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, Ident, ReturnType, ScriptFnDef, Stmt, -}; +pub use ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt}; #[cfg(feature = "internals")] #[deprecated(note = "this type is volatile and may change")] From 746a0b186f6a71d867e3bf373c17344f4a3bd0d2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Dec 2020 15:15:47 +0800 Subject: [PATCH 10/12] Fix typo. --- codegen/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/codegen/src/lib.rs b/codegen/src/lib.rs index c4d50c1f..ea341e1b 100644 --- a/codegen/src/lib.rs +++ b/codegen/src/lib.rs @@ -160,7 +160,7 @@ pub fn export_fn( /// /// let module = exported_module!(my_plugin_module); /// -/// engine.register_global_module(module.into); +/// engine.register_global_module(module.into()); /// /// assert_eq!(engine.eval::("foo(bar())")?, 42); /// # Ok(()) From 08e7ad8c0994e92a3fedb318e25609f76a98e1b2 Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Dec 2020 15:30:35 +0800 Subject: [PATCH 11/12] Add deprecated packages API. --- src/ast.rs | 4 ++-- src/engine_api.rs | 27 ++++++++++++++++++++++++++- src/lib.rs | 8 ++++---- src/packages/mod.rs | 10 ++++++++++ 4 files changed, 42 insertions(+), 7 deletions(-) diff --git a/src/ast.rs b/src/ast.rs index 9a207461..bc801376 100644 --- a/src/ast.rs +++ b/src/ast.rs @@ -228,7 +228,7 @@ impl AST { /// _(INTERNALS)_ Get the statements. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] - #[deprecated(note = "this method is volatile and may change")] + #[deprecated = "this method is volatile and may change"] #[inline(always)] pub fn statements(&self) -> &[Stmt] { &self.statements @@ -255,7 +255,7 @@ impl AST { /// _(INTERNALS)_ Get the internal [`Module`] containing all script-defined functions. /// Exported under the `internals` feature only. #[cfg(feature = "internals")] - #[deprecated(note = "this method is volatile and may change")] + #[deprecated = "this method is volatile and may change"] #[inline(always)] pub fn lib(&self) -> &Module { &self.functions diff --git a/src/engine_api.rs b/src/engine_api.rs index 78164e29..0a38b820 100644 --- a/src/engine_api.rs +++ b/src/engine_api.rs @@ -48,7 +48,7 @@ impl Engine { /// Notice that this will _consume_ the argument, replacing it with `()`. /// /// To access the first mutable parameter, use `args.get_mut(0).unwrap()` - #[deprecated(note = "this function is volatile and may change")] + #[deprecated = "this function is volatile and may change"] #[inline(always)] pub fn register_raw_fn( &mut self, @@ -737,6 +737,16 @@ impl Engine { self.global_modules.insert(0, module); self } + /// Register a shared [`Module`][crate::Module] into the global namespace of [`Engine`]. + /// + /// ## Deprecated + /// + /// Use `register_global_module` instead. + #[inline(always)] + #[deprecated = "use `register_global_module` instead"] + pub fn load_package(&mut self, module: impl Into>) -> &mut Self { + self.register_global_module(module.into()) + } /// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`]. /// /// Functions marked `FnNamespace::Global` and type iterators are exposed to scripts without namespace qualifications. @@ -776,6 +786,21 @@ impl Engine { } self } + /// Register a shared [`Module`][crate::Module] as a static module namespace with the [`Engine`]. + /// + /// ## Deprecated + /// + /// Use `register_static_module` instead. + #[cfg(not(feature = "no_module"))] + #[inline(always)] + #[deprecated = "use `register_static_module` instead"] + pub fn register_module( + &mut self, + name: impl Into, + module: impl Into>, + ) -> &mut Self { + self.register_static_module(name, module.into()) + } /// Compile a string into an [`AST`], which can be used later for evaluation. /// /// # Example diff --git a/src/lib.rs b/src/lib.rs index 946fda30..00f60db2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -174,15 +174,15 @@ pub use optimize::OptimizationLevel; // Expose internal data structures. #[cfg(feature = "internals")] -#[deprecated(note = "this type is volatile and may change")] +#[deprecated = "this type is volatile and may change"] pub use token::{get_next_token, parse_string_literal, InputStream, Token, TokenizeState}; #[cfg(feature = "internals")] -#[deprecated(note = "this type is volatile and may change")] +#[deprecated = "this type is volatile and may change"] pub use ast::{BinaryExpr, CustomExpr, Expr, FnCallExpr, Ident, ReturnType, ScriptFnDef, Stmt}; #[cfg(feature = "internals")] -#[deprecated(note = "this type is volatile and may change")] +#[deprecated = "this type is volatile and may change"] pub use engine::{Imports, State as EvalState}; #[cfg(feature = "internals")] @@ -190,7 +190,7 @@ pub use engine::{Imports, State as EvalState}; pub use engine::Limits; #[cfg(feature = "internals")] -#[deprecated(note = "this type is volatile and may change")] +#[deprecated = "this type is volatile and may change"] pub use module::NamespaceRef; /// _(INTERNALS)_ Alias to [`smallvec::SmallVec<[T; 4]>`](https://crates.io/crates/smallvec), diff --git a/src/packages/mod.rs b/src/packages/mod.rs index 89bb7fbb..9b7112bb 100644 --- a/src/packages/mod.rs +++ b/src/packages/mod.rs @@ -38,6 +38,16 @@ pub trait Package { /// Retrieve the generic package library from this package. fn as_shared_module(&self) -> Shared; + + /// Retrieve the generic package library from this package. + /// + /// ## Deprecated + /// + /// Use `as_shared_module` instead. + #[deprecated = "use `as_shared_module` instead"] + fn get(&self) -> Shared { + self.as_shared_module() + } } /// Macro that makes it easy to define a _package_ (which is basically a shared module) From 7d58324ad42d1aea47d2cb6dd57a4894d13715cf Mon Sep 17 00:00:00 2001 From: Stephen Chung Date: Wed, 23 Dec 2020 19:11:41 +0800 Subject: [PATCH 12/12] Revise package terminology. --- doc/src/SUMMARY.md | 30 ++++++++-------- doc/src/language/fn-namespaces.md | 20 ++++++----- doc/src/patterns/enums.md | 2 +- doc/src/patterns/singleton.md | 2 +- doc/src/plugins/module.md | 4 +-- doc/src/rust/modules/create.md | 27 +++++++------- doc/src/rust/modules/index.md | 25 ++++++++++--- doc/src/rust/packages/builtin.md | 8 ++--- doc/src/rust/packages/create.md | 48 ++++++++++++------------- doc/src/rust/packages/index.md | 58 ++++++++++++++++++++----------- 10 files changed, 129 insertions(+), 95 deletions(-) diff --git a/doc/src/SUMMARY.md b/doc/src/SUMMARY.md index 4adec297..45c199df 100644 --- a/doc/src/SUMMARY.md +++ b/doc/src/SUMMARY.md @@ -41,17 +41,17 @@ The Rhai Scripting Language 2. [Indexers](rust/indexers.md) 3. [Disable Custom Types](rust/disable-custom.md) 4. [Printing Custom Types](rust/print-custom.md) - 9. [Packages](rust/packages/index.md) - 1. [Built-in Packages](rust/packages/builtin.md) - 2. [Custom Packages](rust/packages/create.md) - 10. [Modules](rust/modules/index.md) + 9. [Modules](rust/modules/index.md) 1. [Create from Rust](rust/modules/create.md) 2. [Create from AST](rust/modules/ast.md) 3. [Module Resolvers](rust/modules/resolvers.md) - 1. [Custom Implementation](rust/modules/imp-resolver.md) - 11. [Plugins](plugins/index.md) + 1. [Custom Module Resolvers](rust/modules/imp-resolver.md) + 10. [Plugins](plugins/index.md) 1. [Export a Rust Module](plugins/module.md) 2. [Export a Rust Function](plugins/function.md) + 11. [Packages](rust/packages/index.md) + 1. [Built-in Packages](rust/packages/builtin.md) + 2. [Custom Packages](rust/packages/create.md) 5. [Rhai Language Reference](language/index.md) 1. [Comments](language/comments.md) 1. [Doc-Comments](language/doc-comments.md) @@ -127,18 +127,18 @@ The Rhai Scripting Language 7. [One Engine Instance Per Call](patterns/parallel.md) 8. [Scriptable Event Handler with State](patterns/events.md) 9. [Dynamic Constants Provider](patterns/dynamic-const.md) -9. [Advanced Topics](advanced.md) - 1. [Capture Scope for Function Call](language/fn-capture.md) - 2. [Low-Level API](rust/register-raw.md) - 3. [Variable Resolver](engine/var.md) - 4. [Use as DSL](engine/dsl.md) +9. [Advanced Topics](advanced.md) + 10. [Capture Scope for Function Call](language/fn-capture.md) + 11. [Low-Level API](rust/register-raw.md) + 12. [Variable Resolver](engine/var.md) + 13. [Use as DSL](engine/dsl.md) 1. [Disable Keywords and/or Operators](engine/disable.md) 2. [Custom Operators](engine/custom-op.md) 3. [Extending with Custom Syntax](engine/custom-syntax.md) - 5. [Multiple Instantiation](patterns/multiple.md) - 6. [Functions Metadata](engine/metadata/index.md) - 1. [Generate Function Signatures](engine/metadata/gen_fn_sig.md) - 2. [Export Metadata to JSON](engine/metadata/export_to_json.md) + 14. [Multiple Instantiation](patterns/multiple.md) + 15. [Functions Metadata](engine/metadata/index.md) + 4. [Generate Function Signatures](engine/metadata/gen_fn_sig.md) + 5. [Export Metadata to JSON](engine/metadata/export_to_json.md) 10. [Appendix](appendix/index.md) 1. [Keywords](appendix/keywords.md) 2. [Operators and Symbols](appendix/operators.md) diff --git a/doc/src/language/fn-namespaces.md b/doc/src/language/fn-namespaces.md index ef4cbd77..95bcfb41 100644 --- a/doc/src/language/fn-namespaces.md +++ b/doc/src/language/fn-namespaces.md @@ -15,18 +15,20 @@ allow combining all functions in one [`AST`] into another, forming a new, unifie In general, there are two types of _namespaces_ where functions are looked up: -| Namespace | How Many | Source | Lookup method | Sub-modules? | Variables? | -| --------- | :------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------- | :----------: | :--------: | -| Global | One | 1) [`AST`] being evaluated
2) `Engine::register_XXX` API
3) global [modules] loaded via `Engine::register_global_module`
4) functions in static [modules] loaded via `Engine::register_static_module` and marked _global_ | simple function name | ignored | ignored | -| Module | Many | [`Module`] | namespace-qualified function name | yes | yes | +| Namespace | How Many | Source | Lookup | Sub-modules? | Variables? | +| --------- | :------: | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------ | :----------: | :--------: | +| Global | One | 1) [`AST`] being evaluated
2) `Engine::register_XXX` API
3) global [modules] registered via `Engine::register_global_module`
4) functions in static [modules] registered via `Engine::register_static_module` and marked _global_ | simple name | ignored | ignored | +| Module | Many | 1) [Module] registered via `Engine::register_static_module`
2) [Module] loaded via [`import`] statement | namespace-qualified name | yes | yes | -Module Namespace ----------------- +Module Namespaces +----------------- -There can be multiple module namespaces at any time during a script evaluation, loaded via the +There can be multiple module namespaces at any time during a script evaluation, usually loaded via the [`import`] statement. +_Static_ module namespaces can also be registered into an [`Engine`] via `Engine::register_static_module`. + Functions and variables in module namespaces are isolated and encapsulated within their own environments. They must be called or accessed in a _namespace-qualified_ manner. @@ -55,8 +57,8 @@ There is one _global_ namespace for every [`Engine`], which includes (in the fol * All functions and iterators defined in global [modules] that are registered into the [`Engine`] via `Engine::register_global_module`. -* Functions defined in [modules] loaded via `Engine::register_static_module` that are specifically marked - for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]). +* Functions defined in [modules] registered via `Engine::register_static_module` that are specifically + marked for exposure to the global namespace (e.g. via the `#[rhai(global)]` attribute in a [plugin module]). Anywhere in a Rhai script, when a function call is made, the function is searched within the global namespace, in the above search order. diff --git a/doc/src/patterns/enums.md b/doc/src/patterns/enums.md index 6adeb6d4..31d1c6f9 100644 --- a/doc/src/patterns/enums.md +++ b/doc/src/patterns/enums.md @@ -116,7 +116,7 @@ let mut engine = Engine::new(); // Load the module as the module namespace "MyEnum" engine .register_type_with_name::("MyEnum") - .register_static_module("MyEnum", exported_module!(MyEnumModule)); + .register_static_module("MyEnum", exported_module!(MyEnumModule).into()); ``` With this API in place, working with enums feels almost the same as in Rust: diff --git a/doc/src/patterns/singleton.md b/doc/src/patterns/singleton.md index 316ab302..97b7d221 100644 --- a/doc/src/patterns/singleton.md +++ b/doc/src/patterns/singleton.md @@ -146,7 +146,7 @@ pub mod bunny_api { } } -engine.register_global_module(exported_module!(bunny_api)); +engine.register_global_module(exported_module!(bunny_api).into()); ``` ### Push Constant Command Object into Custom Scope diff --git a/doc/src/plugins/module.md b/doc/src/plugins/module.md index a6b3b8af..c6ea73a0 100644 --- a/doc/src/plugins/module.md +++ b/doc/src/plugins/module.md @@ -101,7 +101,7 @@ fn main() { let module = exported_module!(my_module); // A module can simply be registered into the global namespace. - engine.register_global_module(module); + engine.register_global_module(module.into()); } ``` @@ -140,7 +140,7 @@ fn main() { let module = exported_module!(my_module); // A module can simply be registered as a static module namespace. - engine.register_static_module("service", module); + engine.register_static_module("service", module.into()); } ``` diff --git a/doc/src/rust/modules/create.md b/doc/src/rust/modules/create.md index d0af3a55..d31ccf42 100644 --- a/doc/src/rust/modules/create.md +++ b/doc/src/rust/modules/create.md @@ -19,8 +19,8 @@ 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` Globally Available ------------------------------------ +Use Case 1 - Make the `Module` Globally Available +------------------------------------------------ `Engine::register_global_module` registers a shared [module] into the _global_ namespace. @@ -44,14 +44,14 @@ module.update_fn_metadata(hash, ["x: i64", "i64"]); // Register the module into the global namespace of the Engine. let mut engine = Engine::new(); -engine.register_global_module(module); +engine.register_global_module(module.into()); engine.eval::("inc(41)")? == 42; // no need to import module ``` -Make the `Module` a Static Module --------------------------------- +Use Case 2 - Make the `Module` a Static Module +--------------------------------------------- `Engine::register_static_module` registers a [module] and under a specific module namespace. @@ -69,15 +69,18 @@ module.update_fn_metadata(hash, ["x: i64", "i64"]); // Register the module into the Engine as a static module namespace 'calc' let mut engine = Engine::new(); -engine.register_static_module("calc", module); +engine.register_static_module("calc", module.into()); engine.eval::("calc::inc(41)")? == 42; // refer to the 'Calc' module ``` -`Module::set_fn_XXX_mut` can expose functions (usually _methods_) in the module -to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] can work as expected. +### Expose Functions to the Global Namespace -[Type iterators], because of their special nature, are always exposed to the _global_ namespace. +`Module::set_fn_mut` and `Module::set_fn_XXX_mut` can optionally expose functions (usually _methods_) +in the module to the _global_ namespace, so [getters/setters] and [indexers] for [custom types] +can work as expected. + +[Type iterators], because of their special nature, are _always_ exposed to the _global_ namespace. ```rust use rhai::{Engine, Module, FnNamespace}; @@ -93,15 +96,15 @@ module.update_fn_metadata(hash, ["x: &mut i64", "i64"]); // Register the module into the Engine as a static module namespace 'calc' let mut engine = Engine::new(); -engine.register_static_module("calc", module); +engine.register_static_module("calc", module.into()); // The method 'inc' works as expected because it is exposed to the global namespace engine.eval::("let x = 41; x.inc()")? == 42; ``` -Make the `Module` Dynamically Loadable -------------------------------------- +Use Case 3 - Make the `Module` Dynamically Loadable +-------------------------------------------------- In order to dynamically load a custom module, there must be a [module resolver] which serves the module when loaded via `import` statements. diff --git a/doc/src/rust/modules/index.md b/doc/src/rust/modules/index.md index dd8c928a..b42aa030 100644 --- a/doc/src/rust/modules/index.md +++ b/doc/src/rust/modules/index.md @@ -3,12 +3,27 @@ Modules {{#include ../../links.md}} -Rhai allows organizing code (functions, both Rust-based or script-based, and variables) into _modules_. -Modules can be disabled via the [`no_module`] feature. +Rhai allows organizing functionalities (functions, both Rust-based or script-based, and variables) +into independent _modules_. Modules can be disabled via the [`no_module`] feature. -A module is of the type `Module` and holds a collection of functions, variables, [type iterators] and sub-modules. -It may be created entirely from Rust functions, or it may encapsulate a Rhai script together with the functions -and variables defined by that script. +A module is of the type `Module` and holds a collection of functions, variables, +[type iterators] and sub-modules. + +It may be created entirely from Rust functions, or it may encapsulate a Rhai script together +with the functions and variables defined by that script. Other scripts can then load this module and use the functions and variables exported as if they were defined inside the same script. + +Alternatively, modules can be registered directly into an [`Engine`] and made available +to scripts either globally or under individual static module [_namespaces_][function namespaces]. + + +Usage Patterns +-------------- + +| Usage | API | Lookup | Sub-modules? | Variables? | +| -------------- | :-------------------------------: | :----------------------: | :----------: | :--------: | +| Global module | `Engine:: register_global_module` | simple name | ignored | ignored | +| Static module | `Engine:: register_static_module` | namespace-qualified name | yes | yes | +| Dynamic module | [`import`] statement | namespace-qualified name | yes | yes | diff --git a/doc/src/rust/packages/builtin.md b/doc/src/rust/packages/builtin.md index 5d49ca0a..a9b07b6c 100644 --- a/doc/src/rust/packages/builtin.md +++ b/doc/src/rust/packages/builtin.md @@ -23,10 +23,10 @@ Built-In Packages | `StandardPackage` | standard library (default for `Engine::new`) | no | yes | -Load the `CorePackage` ---------------------- +`CorePackage` +------------- -If only minimal functionalities is required, load the `CorePackage` instead: +If only minimal functionalities are required, register the `CorePackage` instead: ```rust use rhai::Engine; @@ -35,6 +35,6 @@ use rhai::packages::{Package, CorePackage}; let mut engine = Engine::new_raw(); let package = CorePackage::new(); -// Register the package into the Engine by converting it into a shared module. +// Register the package into the 'Engine' by converting it into a shared module. engine.register_global_module(package.as_shared_module()); ``` diff --git a/doc/src/rust/packages/create.md b/doc/src/rust/packages/create.md index 9b3f654a..b02fe61d 100644 --- a/doc/src/rust/packages/create.md +++ b/doc/src/rust/packages/create.md @@ -3,33 +3,31 @@ Create a Custom Package {{#include ../../links.md}} -Sometimes specific functionalities are needed, so custom packages can be created. -A custom package is a convenient means to gather up a number of functions for later use. -An [`Engine`] only needs to `Engine::register_global_module` the custom package once to gain access -to the entire set of functions within. +The macro `def_package!` can be used to create a custom [package]. -Registering a package into an [`Engine`] is functionally equivalent to calling `Engine::register_fn` etc. -on _each_ of the functions inside the package. But because packages are _shared_, using a package is -_much_ cheaper than registering all the functions one by one. - -The macro `rhai::def_package!` can be used to create a new custom package. +A custom package can aggregate many other packages into a single self-contained unit. +More functions can be added on top of others. -Macro Parameters ---------------- +`def_package!` +-------------- -`def_package!(root:package_name:description, variable, block)` +> `def_package!(root:package_name:description, variable, block)` -* `root` - root namespace, usually `"rhai"`. +where: -* `package_name` - name of the package, usually ending in `Package`. +| Parameter | Description | +| :------------: | ----------------------------------------------------------------------------------------------- | +| `root` | root namespace, usually `rhai` | +| `package_name` | name of the package, usually ending in `...Package` | +| `description` | doc-comment for the package | +| `variable` | a variable name holding a reference to the [module] (`&mut Module`) that is to form the package | +| `block` | a code block that initializes the package | -* `description` - doc comment for the package. -* `variable` - a variable name holding a reference to the [module] that is to form the package. - -* `block` - a code block that initializes the package. +Examples +-------- ```rust // Import necessary types and traits. @@ -43,7 +41,7 @@ use rhai::{ // Define the package 'MyPackage'. def_package!(rhai:MyPackage:"My own personal super package", module, { - // Aggregate existing packages simply by calling 'init' on each. + // Aggregate other packages simply by calling 'init' on each. ArithmeticPackage::init(module); LogicPackage::init(module); BasicArrayPackage::init(module); @@ -64,14 +62,14 @@ def_package!(rhai:MyPackage:"My own personal super package", module, { Create a Custom Package from a Plugin Module ------------------------------------------- -By far the easiest way to create a custom module is to call `rhai::plugin::combine_with_exported_module!` -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 `plugin::combine_with_exported_module!` +from within `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. -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. +Due to specific requirements of a [package], `plugin::combine_with_exported_module!` +_flattens_ all sub-modules (i.e. all functions and [type iterators] defined within sub-modules +are pulled up to the top level instead) and so there will not be any sub-modules added to the package. Variables in the [plugin module] are ignored. @@ -107,7 +105,7 @@ mod my_module { // Define the package 'MyPackage'. def_package!(rhai:MyPackage:"My own personal super package", module, { - // Aggregate existing packages simply by calling 'init' on each. + // Aggregate other packages simply by calling 'init' on each. ArithmeticPackage::init(module); LogicPackage::init(module); BasicArrayPackage::init(module); diff --git a/doc/src/rust/packages/index.md b/doc/src/rust/packages/index.md index 541e6f49..afbba8cf 100644 --- a/doc/src/rust/packages/index.md +++ b/doc/src/rust/packages/index.md @@ -3,40 +3,56 @@ Packages {{#include ../../links.md}} -Standard built-in Rhai features are provided in various _packages_ that can be registered into the +The built-in library of Rhai is provided as various _packages_ that can be +turned into _shared_ [modules], which in turn can be registered into the _global namespace_ of an [`Engine`] via `Engine::register_global_module`. -Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` must be loaded in order for -packages to be used. +Packages reside under `rhai::packages::*` and the trait `rhai::packages::Package` +must be loaded in order for packages to be used. + +### Packages _are_ Modules + +Internally, a _package_ is a _newtype_ wrapping a pre-defined [module], +with some conveniences to make it easier to define and use as a standard +_library_ for an [`Engine`]. Packages typically contain Rust functions that are callable within a Rhai script. All _top-level_ functions in a package are available under the _global namespace_ (i.e. they're available without namespace qualifiers). -Once a package is created (e.g. via `Package::new`), it can be _shared_ (via `Package::as_shared_module`) -among multiple instances of [`Engine`], even across threads (under [`sync`]). -Therefore, a package only has to be created _once_. +Sub-modules and variables are ignored in packages. + + +Share a Package Among Multiple `Engine`'s +---------------------------------------- + +`Engine::register_global_module` and `Engine::register_static_module` both require _shared_ [modules]. + +Once a package is created (e.g. via `Package::new`), it can create _shared_ [modules] +(via `Package::as_shared_module`) and register them into multiple instances of [`Engine`], +even across threads (under the [`sync`] feature). + +Therefore, a package only has to be created _once_ and essentially shared among multiple +[`Engine`] instances. This is particular useful when spawning large number of [raw `Engine`'s][raw `Engine`]. ```rust use rhai::Engine; use rhai::packages::Package // load the 'Package' trait to use packages use rhai::packages::CorePackage; // the 'core' package contains basic functionalities (e.g. arithmetic) -// Create a 'raw' Engine -let mut engine = Engine::new_raw(); - -// Create a package - can be shared among multiple `Engine` instances +// Create a package - can be shared among multiple 'Engine' instances let package = CorePackage::new(); -// Register the package into the global namespace. -// 'Package::as_shared_module' converts the package into a shared module. -engine.register_global_module(package.as_shared_module()); +let mut engines_collection: Vec = Vec::new(); + +// Create 100 'raw' Engines +for _ in 0..100 { + let mut engine = Engine::new_raw(); + + // Register the package into the global namespace. + // 'Package::as_shared_module' converts the package into a shared module. + engine.register_global_module(package.as_shared_module()); + + engines_collection.push(engine); +} ``` - - -Share a Package Among `Engine`s ------------------------------- - -`Engine::register_global_module` consumes the input shared module. - -However, `Package::as_shared_module` can be called multiple times for multiple instances of [`Engine`].