Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add custom types metadata. #756

Merged
merged 8 commits into from
Aug 31, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 13 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,17 +12,27 @@ Bug fixes
* `max` and `min` for integers, strings and characters were missing from the standard library. They are now added.
* O/S features such as file access and time are no longer disabled when using `wasm32-wasi` (or any WASM target other than `wasm32-unknown`).

Enhancements
Dependencies
------------

* [`once_cell`](https://crates.io/crates/once_cell) is used in `std` environments instead of the home-brew `SusLock` (which is still kept for `no-std`).
* Originally, unit tests use the `?` operator liberally to simplify the code. However, this causes the loss of proper line numbers when it fails, making it difficult to identify the exact location of the failure. This is now fixed by using `unwrap()` instead.
* Minimal version numbers for dependencies are now specified in `Cargo.toml` to avoid breaking changes in future versions.
* [`bitflags`](https://crates.io/crates/bitflags) is bumped to version 2.
* [`syn`](https://crates.io/crates/syn) in [`rhai_codegen`](https://crates.io/crates/rhai_codegen) is bumped to version 2.
* [`hashbrown`](https://crates.io/crates/hashbrown) (used in `no-std` builds) is bumped to version 0.14.

New features
------------

* Added `Engine::max_variables` and `Engine::set_max_variables` to limit the maximum number of variables allowed within a scope at any time. This is to guard against defining a huge number of variables containing large data just beyond individual data size limits. When `max_variables` is exceeded a new error, `ErrorTooManyVariables`, is returned.
* Added `zip` function for arrays.
* Doc-comments are now included in custom type definitions within plugin modules. They can be accessed via `Module::get_custom_type_comments`. These doc-comments for custom types are also exported in JSON via `Engine::gen_fn_metadata_to_json`.

Enhancements
------------

* [`once_cell`](https://crates.io/crates/once_cell) is used in `std` environments instead of the home-brew `SusLock` which is removed.
* Originally, unit tests use the `?` operator liberally to simplify code. However, this causes the loss of proper line numbers when a test fails, making it difficult to identify the exact location of the failure. This is now fixed by changing to `unwrap()`.
* Many inlined collections are turned back into `Vec` because they are not transient and do not appear to improve performance. Using `Vec` seems to be yield better performance as it probably enables more compiler optimizations.


Version 1.15.1
Expand Down
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ rhai_codegen = { version = "1.5.0", path = "codegen", default-features = false }

no-std-compat = { git = "https://gitlab.com/jD91mZM2/no-std-compat", version = "0.4.1", default-features = false, features = ["alloc"], optional = true }
libm = { version = "0.2.0", default-features = false, optional = true }
hashbrown = { version = "0.13.1", optional = true }
hashbrown = { version = "0.14.0", optional = true }
core-error = { version = "0.0.0", default-features = false, features = ["alloc"], optional = true }
serde = { version = "1.0.96", default-features = false, features = ["derive", "alloc"], optional = true }
serde_json = { version = "1.0.45", default-features = false, features = ["alloc"], optional = true }
Expand Down
4 changes: 2 additions & 2 deletions codegen/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "rhai_codegen"
version = "1.5.0"
version = "1.6.0"
edition = "2018"
resolver = "2"
authors = ["jhwgh1968", "Stephen Chung"]
Expand All @@ -24,5 +24,5 @@ syn = { version = "2.0.0", features = ["full", "parsing", "printing", "proc-macr
quote = "1.0.0"

[dev-dependencies]
rhai = { path = "..", version = "1.12.0", features = ["metadata"] }
rhai = { path = "..", version = "1.16.0", features = ["metadata"] }
trybuild = "1.0.0"
2 changes: 2 additions & 0 deletions codegen/src/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,8 @@ impl Parse for Module {
name: ident.to_string(),
typ: ty.clone(),
cfg_attrs: crate::attrs::collect_cfg_attr(attrs),
#[cfg(feature = "metadata")]
comments: crate::attrs::doc_attributes(attrs)?,
})
}
}
Expand Down
26 changes: 22 additions & 4 deletions codegen/src/rhai_module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub struct ExportedType {
pub name: String,
pub typ: Box<syn::Type>,
pub cfg_attrs: Vec<syn::Attribute>,
#[cfg(feature = "metadata")]
pub comments: Vec<String>,
}

pub fn generate_body(
Expand Down Expand Up @@ -67,6 +69,8 @@ pub fn generate_body(
name,
typ,
cfg_attrs,
#[cfg(feature = "metadata")]
comments,
..
} in custom_types
{
Expand All @@ -77,13 +81,27 @@ pub fn generate_body(
.map(syn::Attribute::to_token_stream)
.collect();

set_const_statements.push(
#[cfg(feature = "metadata")]
let comments = comments
.iter()
.map(|s| syn::LitStr::new(s, Span::call_site()))
.collect::<Vec<_>>();
#[cfg(not(feature = "metadata"))]
let comments = Vec::<syn::LitStr>::new();

set_const_statements.push(if comments.is_empty() {
syn::parse2::<syn::Stmt>(quote! {
#(#cfg_attrs)*
m.set_custom_type::<#typ>(#const_literal);
})
.unwrap(),
);
.unwrap()
} else {
syn::parse2::<syn::Stmt>(quote! {
#(#cfg_attrs)*
m.set_custom_type_with_comments::<#typ>(#const_literal, &[#(#comments),*]);
})
.unwrap()
});
}

for item_mod in sub_modules {
Expand Down Expand Up @@ -231,7 +249,7 @@ pub fn generate_body(
syn::parse2::<syn::Stmt>(quote! {
#(#cfg_attrs)*
m.set_fn_with_comments(#fn_literal, FnNamespace::#ns_str, FnAccess::Public,
#param_names, [#(#fn_input_types),*], [#(#comments),*], #fn_token_name().into());
#param_names, &[#(#fn_input_types),*], &[#(#comments),*], #fn_token_name().into());
})
.unwrap()
});
Expand Down
20 changes: 18 additions & 2 deletions codegen/src/test/module.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,9 @@ mod module_tests {
pub mod one_fn {
pub type Hello = ();

/// We are the world!
pub type World = String;

pub fn get_mystic_number() -> INT {
42
}
Expand All @@ -51,8 +54,14 @@ mod module_tests {

let item_mod = syn::parse2::<Module>(input_tokens).unwrap();
assert!(item_mod.consts().is_empty());
assert_eq!(item_mod.custom_types().len(), 1);
assert_eq!(item_mod.custom_types().len(), 2);
assert_eq!(item_mod.custom_types()[0].name.to_string(), "Hello");
assert_eq!(item_mod.custom_types()[1].name.to_string(), "World");
#[cfg(feature = "metadata")]
assert_eq!(
item_mod.custom_types()[1].comments[0],
"/// We are the world!"
);
assert_eq!(item_mod.fns().len(), 1);
assert_eq!(item_mod.fns()[0].name().to_string(), "get_mystic_number");
assert_eq!(item_mod.fns()[0].arg_count(), 0);
Expand Down Expand Up @@ -409,6 +418,9 @@ mod generate_tests {
/// Another line!
/// Final line!
pub mod one_fn {
/// We are the world!
pub type World = String;

/// This is a doc-comment.
/// Another line.
/** block doc-comment */
Expand All @@ -432,6 +444,9 @@ mod generate_tests {
/// Final line!
#[allow(clippy::needless_pass_by_value)]
pub mod one_fn {
/// We are the world!
pub type World = String;

/// This is a doc-comment.
/// Another line.
/** block doc-comment */
Expand All @@ -458,10 +473,11 @@ mod generate_tests {
#[doc(hidden)]
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
m.set_fn_with_comments("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
Some(get_mystic_number_token::PARAM_NAMES), [], [
Some(get_mystic_number_token::PARAM_NAMES), &[], &[
"/// This is a doc-comment.\n/// Another line.\n/// block doc-comment \n/// Final line.",
"/** doc-comment\n in multiple lines\n */"
], get_mystic_number_token().into());
m.set_custom_type_with_comments::<String>("World", &["/// We are the world!"]);
if flatten {} else {}
}
#[allow(non_camel_case_types)]
Expand Down
2 changes: 0 additions & 2 deletions codegen/ui_tests/rhai_mod_unknown_type.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -17,5 +17,3 @@ help: consider importing one of these items
|
11 + use std::fmt::Pointer;
|
11 + use syn::__private::fmt::Pointer;
|
3 changes: 1 addition & 2 deletions src/api/custom_syntax.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ use crate::tokenizer::{is_reserved_keyword_or_symbol, is_valid_identifier, Token
use crate::types::dynamic::Variant;
use crate::{
Dynamic, Engine, EvalContext, Identifier, ImmutableString, LexError, Position, RhaiResult,
StaticVec,
};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
Expand Down Expand Up @@ -223,7 +222,7 @@ impl Engine {
#[allow(clippy::wildcard_imports)]
use markers::*;

let mut segments = StaticVec::<ImmutableString>::new();
let mut segments = Vec::<ImmutableString>::new();

for s in symbols.as_ref() {
let s = s.as_ref().trim();
Expand Down
10 changes: 5 additions & 5 deletions src/ast/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -48,9 +48,9 @@
#[derive(Debug, Clone, Hash)]
pub struct CustomExpr {
/// List of keywords.
pub inputs: StaticVec<Expr>,
pub inputs: Box<[Expr]>,
/// List of tokens actually parsed.
pub tokens: StaticVec<ImmutableString>,
pub tokens: Box<[ImmutableString]>,
/// State value.
pub state: Dynamic,
/// Is the current [`Scope`][crate::Scope] possibly modified by this custom statement
Expand Down Expand Up @@ -201,7 +201,7 @@
/// Pre-calculated hashes.
pub hashes: FnCallHashes,
/// List of function call argument expressions.
pub args: FnArgsVec<Expr>,
pub args: Box<[Expr]>,
/// Does this function call capture the parent scope?
pub capture_parent_scope: bool,
/// Is this function call a native operator?
Expand Down Expand Up @@ -585,7 +585,7 @@
/// `non_qualified` is ignored under `no_module`.
#[inline]
#[must_use]
pub(crate) fn is_variable_access(&self, _non_qualified: bool) -> bool {

Check warning on line 588 in src/ast/expr.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_time,no_function,no_float,no_position,no_inde...

method `is_variable_access` is never used

Check warning on line 588 in src/ast/expr.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_optimize,serde,metadata,internals,debugging, ...

method `is_variable_access` is never used

Check warning on line 588 in src/ast/expr.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,sync,no_time,no_function,no_float,no_position,no...

method `is_variable_access` is never used
match self {
#[cfg(not(feature = "no_module"))]
Self::Variable(x, ..) if _non_qualified && !x.1.is_empty() => false,
Expand Down Expand Up @@ -885,15 +885,15 @@
}
}
Self::FnCall(x, ..) => {
for e in &x.args {
for e in x.args.iter() {
if !e.walk(path, on_node) {
return false;
}
}
}
#[cfg(not(feature = "no_custom_syntax"))]
Self::Custom(x, ..) => {
for e in &x.inputs {
for e in x.inputs.iter() {
if !e.walk(path, on_node) {
return false;
}
Expand Down
10 changes: 5 additions & 5 deletions src/ast/stmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ use crate::func::StraightHashMap;
use crate::tokenizer::Token;
use crate::types::dynamic::Union;
use crate::types::Span;
use crate::{calc_fn_hash, Dynamic, Position, StaticVec, INT};
use crate::{calc_fn_hash, Dynamic, Position, INT};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{
Expand Down Expand Up @@ -372,11 +372,11 @@ pub type CaseBlocksList = smallvec::SmallVec<[usize; 1]>;
#[derive(Debug, Clone)]
pub struct SwitchCasesCollection {
/// List of [`ConditionalExpr`]'s.
pub expressions: StaticVec<ConditionalExpr>,
pub expressions: Vec<ConditionalExpr>,
/// Dictionary mapping value hashes to [`ConditionalExpr`]'s.
pub cases: StraightHashMap<CaseBlocksList>,
/// List of range cases.
pub ranges: StaticVec<RangeCase>,
pub ranges: Vec<RangeCase>,
/// Statements block for the default case (there can be no condition for the default case).
pub def_case: Option<usize>,
}
Expand Down Expand Up @@ -410,7 +410,7 @@ pub type StmtBlockContainer = smallvec::SmallVec<[Stmt; STMT_BLOCK_INLINE_SIZE]>
/// _(internals)_ The underlying container type for [`StmtBlock`].
/// Exported under the `internals` feature only.
#[cfg(feature = "no_std")]
pub type StmtBlockContainer = StaticVec<Stmt>;
pub type StmtBlockContainer = crate::StaticVec<Stmt>;

/// _(internals)_ A scoped block of statements.
/// Exported under the `internals` feature only.
Expand Down Expand Up @@ -1139,7 +1139,7 @@ impl Stmt {
}
}
Self::FnCall(x, ..) => {
for s in &x.args {
for s in x.args.iter() {
if !s.walk(path, on_node) {
return false;
}
Expand Down
5 changes: 4 additions & 1 deletion src/bin/rhai-repl.rs
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,9 @@ fn setup_editor() -> DefaultEditor {
/// Module containing sample functions.
#[export_module]
mod sample_functions {
/// My super-string type.
pub type MyType = String;

/// This is a sample function.
///
/// It takes two numbers and prints them to a string.
Expand All @@ -262,7 +265,7 @@ mod sample_functions {
///
/// print(result); // prints "42 123"
/// ```
pub fn test(x: INT, y: INT) -> String {
pub fn test(x: INT, y: INT) -> MyType {
format!("{x} {y}")
}

Expand Down
8 changes: 3 additions & 5 deletions src/defer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,11 +41,9 @@ macro_rules! defer {
};
($var:ident = ( $value:expr ) if Some($guard:ident) => $restore:expr) => {
let mut __rx__;
let $var = if let Some($guard) = $guard {
__rx__ = crate::Deferred::lock($value, $restore);
&mut *__rx__
} else {
&mut *$value
let $var = match $guard {
Some($guard) => { __rx__ = crate::Deferred::lock($value, $restore); &mut *__rx__ }
_ => &mut *$value
};
};
($var:ident if $guard:expr => $restore:expr) => {
Expand Down
8 changes: 3 additions & 5 deletions src/engine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@ use crate::func::native::{
use crate::packages::{Package, StandardPackage};
use crate::tokenizer::Token;
use crate::types::StringsInterner;
use crate::{
Dynamic, Identifier, ImmutableString, Locked, OptimizationLevel, SharedModule, StaticVec,
};
use crate::{Dynamic, Identifier, ImmutableString, Locked, OptimizationLevel, SharedModule};
#[cfg(feature = "no_std")]
use std::prelude::v1::*;
use std::{collections::BTreeSet, fmt, num::NonZeroU8};
Expand Down Expand Up @@ -94,7 +92,7 @@ pub const NAMESPACE_SEPARATOR: &str = Token::DoubleColon.literal_syntax();
/// ```
pub struct Engine {
/// A collection of all modules loaded into the global namespace of the Engine.
pub(crate) global_modules: StaticVec<SharedModule>,
pub(crate) global_modules: Vec<SharedModule>,
/// A collection of all sub-modules directly loaded into the Engine.
#[cfg(not(feature = "no_module"))]
pub(crate) global_sub_modules: Option<std::collections::BTreeMap<Identifier, SharedModule>>,
Expand Down Expand Up @@ -228,7 +226,7 @@ pub fn make_setter(id: &str) -> Identifier {
impl Engine {
/// An empty raw [`Engine`].
pub const RAW: Self = Self {
global_modules: StaticVec::new_const(),
global_modules: Vec::new(),

#[cfg(not(feature = "no_module"))]
global_sub_modules: None,
Expand Down
4 changes: 2 additions & 2 deletions src/eval/chaining.rs
Original file line number Diff line number Diff line change
Expand Up @@ -356,7 +356,7 @@
#[cfg(not(feature = "no_object"))]
(Expr::Property(..), ChainType::Dotting) => (),
#[cfg(not(feature = "no_object"))]
(Expr::Property(..), ..) => {

Check warning on line 359 in src/eval/chaining.rs

View workflow job for this annotation

GitHub Actions / Build (ubuntu-latest, --features testing-environ,no_index,serde,metadata,internals,debugging, sta...

unreachable pattern
unreachable!("unexpected Expr::Property for indexing")
}
// Short-circuit for indexing with literal: {expr}[1]
Expand Down Expand Up @@ -456,7 +456,7 @@
"method call in dot chain should not be namespace-qualified"
);

for expr in &x.args {
for expr in x.args.iter() {
let arg_value =
self.get_arg_value(global, caches, scope, this_ptr.as_deref_mut(), expr)?;
idx_values.push(arg_value.0.flatten());
Expand Down Expand Up @@ -485,7 +485,7 @@
"method call in dot chain should not be namespace-qualified"
);

for expr in &x.args {
for expr in x.args.iter() {
let tp = this_ptr.as_deref_mut();
let arg_value = self.get_arg_value(global, caches, scope, tp, expr)?;
_arg_values.push(arg_value.0.flatten());
Expand Down
3 changes: 1 addition & 2 deletions src/eval/expr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,8 +150,7 @@ impl Engine {
if let Some(fn_def) = global.lib.iter().flat_map(|m| m.iter_script_fn()).find_map(
|(_, _, f, _, func)| if f == v.3.as_str() { Some(func) } else { None },
) {
let mut fn_ptr =
crate::FnPtr::new_unchecked(v.3.clone(), crate::StaticVec::new_const());
let mut fn_ptr = crate::FnPtr::new_unchecked(v.3.clone(), Vec::new());
fn_ptr.set_fn_def(Some(fn_def.clone()));
let val: Dynamic = fn_ptr.into();
return Ok(val.into());
Expand Down
Loading
Loading