Add type alias support for plugin modules.
This commit is contained in:
parent
6546eae95f
commit
fefa633cf0
@ -1,6 +1,6 @@
|
||||
[package]
|
||||
name = "rhai_codegen"
|
||||
version = "1.3.0"
|
||||
version = "1.4.0"
|
||||
edition = "2018"
|
||||
authors = ["jhwgh1968", "Stephen Chung"]
|
||||
description = "Procedural macros support package for Rhai, a scripting language and engine for Rust"
|
||||
|
@ -10,7 +10,7 @@ use std::borrow::Cow;
|
||||
|
||||
use crate::attrs::{AttrItem, ExportInfo, ExportScope, ExportedParams};
|
||||
use crate::function::ExportedFn;
|
||||
use crate::rhai_module::ExportedConst;
|
||||
use crate::rhai_module::{ExportedConst, ExportedType};
|
||||
|
||||
#[derive(Debug, Clone, Eq, PartialEq, Hash, Default)]
|
||||
pub struct ExportedModParams {
|
||||
@ -89,8 +89,9 @@ impl ExportedParams for ExportedModParams {
|
||||
#[derive(Debug)]
|
||||
pub struct Module {
|
||||
mod_all: syn::ItemMod,
|
||||
fns: Vec<ExportedFn>,
|
||||
consts: Vec<ExportedConst>,
|
||||
custom_types: Vec<ExportedType>,
|
||||
fns: Vec<ExportedFn>,
|
||||
sub_modules: Vec<Module>,
|
||||
params: ExportedModParams,
|
||||
}
|
||||
@ -107,6 +108,7 @@ impl Parse for Module {
|
||||
let mut mod_all: syn::ItemMod = input.parse()?;
|
||||
let fns: Vec<_>;
|
||||
let mut consts = Vec::new();
|
||||
let mut custom_types = Vec::new();
|
||||
let mut sub_modules = Vec::new();
|
||||
|
||||
if let Some((.., ref mut content)) = mod_all.content {
|
||||
@ -155,6 +157,25 @@ impl Parse for Module {
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Gather and parse type definitions.
|
||||
for item in content.iter() {
|
||||
match item {
|
||||
syn::Item::Type(syn::ItemType {
|
||||
vis,
|
||||
ident,
|
||||
attrs,
|
||||
ty,
|
||||
..
|
||||
}) if matches!(vis, syn::Visibility::Public(..)) => {
|
||||
custom_types.push(ExportedType {
|
||||
name: ident.to_string(),
|
||||
typ: ty.clone(),
|
||||
cfg_attrs: crate::attrs::collect_cfg_attr(&attrs),
|
||||
})
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
// Gather and parse sub-module definitions.
|
||||
//
|
||||
// They are actually removed from the module's body, because they will need
|
||||
@ -188,6 +209,7 @@ impl Parse for Module {
|
||||
mod_all,
|
||||
fns,
|
||||
consts,
|
||||
custom_types,
|
||||
sub_modules,
|
||||
params: ExportedModParams::default(),
|
||||
})
|
||||
@ -241,6 +263,7 @@ impl Module {
|
||||
mut mod_all,
|
||||
mut fns,
|
||||
consts,
|
||||
custom_types,
|
||||
mut sub_modules,
|
||||
params,
|
||||
..
|
||||
@ -257,6 +280,7 @@ impl Module {
|
||||
let mod_gen = crate::rhai_module::generate_body(
|
||||
&mut fns,
|
||||
&consts,
|
||||
&custom_types,
|
||||
&mut sub_modules,
|
||||
¶ms.scope,
|
||||
);
|
||||
@ -300,6 +324,11 @@ impl Module {
|
||||
&self.consts
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn custom_types(&self) -> &[ExportedType] {
|
||||
&self.custom_types
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub fn fns(&self) -> &[ExportedFn] {
|
||||
&self.fns
|
||||
|
@ -18,9 +18,17 @@ pub struct ExportedConst {
|
||||
pub cfg_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ExportedType {
|
||||
pub name: String,
|
||||
pub typ: Box<syn::Type>,
|
||||
pub cfg_attrs: Vec<syn::Attribute>,
|
||||
}
|
||||
|
||||
pub fn generate_body(
|
||||
fns: &mut [ExportedFn],
|
||||
consts: &[ExportedConst],
|
||||
custom_types: &[ExportedType],
|
||||
sub_modules: &mut [Module],
|
||||
parent_scope: &ExportScope,
|
||||
) -> TokenStream {
|
||||
@ -54,6 +62,29 @@ pub fn generate_body(
|
||||
);
|
||||
}
|
||||
|
||||
for ExportedType {
|
||||
name,
|
||||
typ,
|
||||
cfg_attrs,
|
||||
..
|
||||
} in custom_types
|
||||
{
|
||||
let const_literal = syn::LitStr::new(&name, Span::call_site());
|
||||
|
||||
let cfg_attrs: Vec<_> = cfg_attrs
|
||||
.iter()
|
||||
.map(syn::Attribute::to_token_stream)
|
||||
.collect();
|
||||
|
||||
set_const_statements.push(
|
||||
syn::parse2::<syn::Stmt>(quote! {
|
||||
#(#cfg_attrs)*
|
||||
m.set_custom_type::<#typ>(#const_literal);
|
||||
})
|
||||
.unwrap(),
|
||||
);
|
||||
}
|
||||
|
||||
for item_mod in sub_modules {
|
||||
item_mod.update_scope(&parent_scope);
|
||||
if item_mod.skipped() {
|
||||
|
@ -37,6 +37,31 @@ mod module_tests {
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn one_factory_fn_with_custom_type_module() {
|
||||
let input_tokens: TokenStream = quote! {
|
||||
pub mod one_fn {
|
||||
pub type Hello = ();
|
||||
|
||||
pub fn get_mystic_number() -> INT {
|
||||
42
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
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()[0].name.to_string(), "Hello");
|
||||
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);
|
||||
assert_eq!(
|
||||
item_mod.fns()[0].return_type().unwrap(),
|
||||
&syn::parse2::<syn::Type>(quote! { INT }).unwrap()
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[cfg(feature = "metadata")]
|
||||
fn one_factory_fn_with_comments_module() {
|
||||
@ -753,7 +778,13 @@ mod generate_tests {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Foo(pub INT);
|
||||
|
||||
pub type Hello = Foo;
|
||||
|
||||
pub const MYSTIC_NUMBER: Foo = Foo(42);
|
||||
|
||||
pub fn get_mystic_number(x: &mut Hello) -> INT {
|
||||
x.0
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@ -762,7 +793,13 @@ mod generate_tests {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct Foo(pub INT);
|
||||
|
||||
pub type Hello = Foo;
|
||||
|
||||
pub const MYSTIC_NUMBER: Foo = Foo(42);
|
||||
|
||||
pub fn get_mystic_number(x: &mut Hello) -> INT {
|
||||
x.0
|
||||
}
|
||||
#[allow(unused_imports)]
|
||||
use super::*;
|
||||
|
||||
@ -774,9 +811,32 @@ mod generate_tests {
|
||||
}
|
||||
#[allow(unused_mut)]
|
||||
pub fn rhai_generate_into_module(m: &mut Module, flatten: bool) {
|
||||
m.set_fn("get_mystic_number", FnNamespace::Internal, FnAccess::Public,
|
||||
Some(get_mystic_number_token::PARAM_NAMES), &[TypeId::of::<Hello>()],
|
||||
get_mystic_number_token().into());
|
||||
m.set_var("MYSTIC_NUMBER", MYSTIC_NUMBER);
|
||||
m.set_custom_type::<Foo>("Hello");
|
||||
if flatten {} else {}
|
||||
}
|
||||
|
||||
#[allow(non_camel_case_types)]
|
||||
pub struct get_mystic_number_token();
|
||||
impl get_mystic_number_token {
|
||||
pub const PARAM_NAMES: &'static [&'static str] = &["x: &mut Hello", "INT"];
|
||||
#[inline(always)] pub fn param_types() -> [TypeId; 1usize] { [TypeId::of::<Hello>()] }
|
||||
}
|
||||
impl PluginFunction for get_mystic_number_token {
|
||||
#[inline(always)]
|
||||
fn call(&self, context: NativeCallContext, args: &mut [&mut Dynamic]) -> RhaiResult {
|
||||
if args[0usize].is_read_only() {
|
||||
return Err(EvalAltResult::ErrorAssignmentToConstant("x".to_string(), Position::NONE).into());
|
||||
}
|
||||
let arg0 = &mut args[0usize].write_lock::<Hello>().unwrap();
|
||||
Ok(Dynamic::from(get_mystic_number(arg0)))
|
||||
}
|
||||
|
||||
#[inline(always)] fn is_method_call(&self) -> bool { true }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -10,7 +10,7 @@ use std::any::{type_name, TypeId};
|
||||
use std::prelude::v1::*;
|
||||
|
||||
impl Engine {
|
||||
/// Get the global namespace module (which is the last module in `global_modules`).
|
||||
/// Get the global namespace module (which is the fist module in `global_modules`).
|
||||
#[inline(always)]
|
||||
#[allow(dead_code)]
|
||||
pub(crate) fn global_namespace(&self) -> &Module {
|
||||
@ -273,7 +273,8 @@ impl Engine {
|
||||
/// ```
|
||||
#[inline(always)]
|
||||
pub fn register_type_with_name<T: Variant + Clone>(&mut self, name: &str) -> &mut Self {
|
||||
self.register_type_with_name_raw(type_name::<T>(), name)
|
||||
self.custom_types.add_type::<T>(name);
|
||||
self
|
||||
}
|
||||
/// Register a custom type for use with the [`Engine`], with a pretty-print name
|
||||
/// for the `type_of` function. The type must implement [`Clone`].
|
||||
@ -288,8 +289,7 @@ impl Engine {
|
||||
name: impl Into<Identifier>,
|
||||
) -> &mut Self {
|
||||
// Add the pretty-print type name into the map
|
||||
self.type_names
|
||||
.insert(fully_qualified_type_path.into(), name.into());
|
||||
self.custom_types.add(fully_qualified_type_path, name);
|
||||
self
|
||||
}
|
||||
/// Register an type iterator for an iterable type with the [`Engine`].
|
||||
|
@ -85,9 +85,15 @@ impl Engine {
|
||||
#[inline]
|
||||
#[must_use]
|
||||
pub fn map_type_name<'a>(&'a self, name: &'a str) -> &'a str {
|
||||
self.type_names
|
||||
.get(name)
|
||||
.map(|s| s.as_str())
|
||||
self.global_modules
|
||||
.iter()
|
||||
.find_map(|m| m.get_custom_type(name))
|
||||
.or_else(|| {
|
||||
self.global_sub_modules
|
||||
.iter()
|
||||
.find_map(|(_, m)| m.get_custom_type(name))
|
||||
})
|
||||
.or_else(|| self.custom_types.get(name))
|
||||
.unwrap_or_else(|| map_std_type_name(name, true))
|
||||
}
|
||||
|
||||
@ -109,9 +115,8 @@ impl Engine {
|
||||
};
|
||||
}
|
||||
|
||||
self.type_names
|
||||
self.custom_types
|
||||
.get(name)
|
||||
.map(|s| s.as_str())
|
||||
.unwrap_or_else(|| match name {
|
||||
"INT" => return type_name::<crate::INT>(),
|
||||
#[cfg(not(feature = "no_float"))]
|
||||
|
@ -6,7 +6,7 @@ use crate::func::native::{
|
||||
};
|
||||
use crate::packages::{Package, StandardPackage};
|
||||
use crate::tokenizer::Token;
|
||||
use crate::types::dynamic::Union;
|
||||
use crate::types::{dynamic::Union, CustomTypesCollection};
|
||||
use crate::{
|
||||
Dynamic, Identifier, ImmutableString, Module, OptimizationLevel, Position, RhaiResult, Shared,
|
||||
StaticVec,
|
||||
@ -105,7 +105,7 @@ pub struct Engine {
|
||||
pub(crate) module_resolver: Option<Box<dyn crate::ModuleResolver>>,
|
||||
|
||||
/// A map mapping type names to pretty-print names.
|
||||
pub(crate) type_names: BTreeMap<Identifier, Identifier>,
|
||||
pub(crate) custom_types: CustomTypesCollection,
|
||||
|
||||
/// An empty [`ImmutableString`] for cloning purposes.
|
||||
pub(crate) empty_string: ImmutableString,
|
||||
@ -160,7 +160,7 @@ impl fmt::Debug for Engine {
|
||||
f.field("global_sub_modules", &self.global_sub_modules)
|
||||
.field("module_resolver", &self.module_resolver.is_some());
|
||||
|
||||
f.field("type_names", &self.type_names)
|
||||
f.field("type_names", &self.custom_types)
|
||||
.field("disabled_symbols", &self.disabled_symbols)
|
||||
.field("custom_keywords", &self.custom_keywords)
|
||||
.field("custom_syntax", &(!self.custom_syntax.is_empty()))
|
||||
@ -271,7 +271,7 @@ impl Engine {
|
||||
#[cfg(not(feature = "no_module"))]
|
||||
module_resolver: None,
|
||||
|
||||
type_names: BTreeMap::new(),
|
||||
custom_types: CustomTypesCollection::new(),
|
||||
empty_string: ImmutableString::new(),
|
||||
disabled_symbols: BTreeSet::new(),
|
||||
custom_keywords: BTreeMap::new(),
|
||||
|
@ -5,7 +5,7 @@ use crate::func::{
|
||||
shared_take_or_clone, CallableFunction, FnCallArgs, IteratorFn, RegisterNativeFunction,
|
||||
SendSync,
|
||||
};
|
||||
use crate::types::dynamic::Variant;
|
||||
use crate::types::{dynamic::Variant, CustomTypesCollection};
|
||||
use crate::{
|
||||
calc_fn_params_hash, calc_qualified_fn_hash, combine_hashes, Dynamic, Identifier,
|
||||
ImmutableString, NativeCallContext, RhaiResultOf, Shared, StaticVec,
|
||||
@ -232,6 +232,8 @@ pub struct Module {
|
||||
pub(crate) internal: bool,
|
||||
/// Is this module part of a standard library?
|
||||
pub(crate) standard: bool,
|
||||
/// Custom types.
|
||||
custom_types: CustomTypesCollection,
|
||||
/// Sub-modules.
|
||||
modules: BTreeMap<Identifier, Shared<Module>>,
|
||||
/// [`Module`] variables.
|
||||
@ -339,6 +341,7 @@ impl Module {
|
||||
id: Identifier::new_const(),
|
||||
internal: false,
|
||||
standard: false,
|
||||
custom_types: CustomTypesCollection::new(),
|
||||
modules: BTreeMap::new(),
|
||||
variables: BTreeMap::new(),
|
||||
all_variables: BTreeMap::new(),
|
||||
@ -413,6 +416,17 @@ impl Module {
|
||||
self
|
||||
}
|
||||
|
||||
/// Map a custom type to a friendly display name.
|
||||
#[inline(always)]
|
||||
pub fn set_custom_type<T>(&mut self, name: &str) {
|
||||
self.custom_types.add_type::<T>(name)
|
||||
}
|
||||
/// Get the display name of a registered custom type.
|
||||
#[inline(always)]
|
||||
pub fn get_custom_type(&self, key: &str) -> Option<&str> {
|
||||
self.custom_types.get(key)
|
||||
}
|
||||
|
||||
/// Is the [`Module`] empty?
|
||||
///
|
||||
/// # Example
|
||||
|
43
src/types/custom_types.rs
Normal file
43
src/types/custom_types.rs
Normal file
@ -0,0 +1,43 @@
|
||||
//! Collection of custom types.
|
||||
|
||||
use crate::Identifier;
|
||||
use std::{any::type_name, collections::BTreeMap, fmt};
|
||||
|
||||
/// _(internals)_ A custom type.
|
||||
/// Exported under the `internals` feature only.
|
||||
pub type CustomType = Identifier;
|
||||
|
||||
/// _(internals)_ A collection of custom types.
|
||||
/// Exported under the `internals` feature only.
|
||||
#[derive(Clone, Hash, Default)]
|
||||
pub struct CustomTypesCollection(BTreeMap<Identifier, CustomType>);
|
||||
|
||||
impl fmt::Debug for CustomTypesCollection {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str("CustomTypesCollection ")?;
|
||||
f.debug_map().entries(self.0.iter()).finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl CustomTypesCollection {
|
||||
/// Create a new [`CustomTypesCollection`].
|
||||
#[inline(always)]
|
||||
pub fn new() -> Self {
|
||||
Self(BTreeMap::new())
|
||||
}
|
||||
/// Register a custom type.
|
||||
#[inline(always)]
|
||||
pub fn add(&mut self, key: impl Into<Identifier>, name: impl Into<Identifier>) {
|
||||
self.0.insert(key.into(), name.into());
|
||||
}
|
||||
/// Register a custom type.
|
||||
#[inline(always)]
|
||||
pub fn add_type<T>(&mut self, name: &str) {
|
||||
self.0.insert(type_name::<T>().into(), name.into());
|
||||
}
|
||||
/// Find a custom type.
|
||||
#[inline(always)]
|
||||
pub fn get(&self, key: &str) -> Option<&str> {
|
||||
self.0.get(key).map(CustomType::as_str)
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
//! Helper module which defines the [`Any`] trait to to allow dynamic value handling.
|
||||
//! Helper module which defines the [`Dynamic`] data type and the
|
||||
//! [`Any`] trait to to allow custom type handling.
|
||||
|
||||
use crate::func::native::SendSync;
|
||||
use crate::{reify, ExclusiveRange, FnPtr, ImmutableString, InclusiveRange, INT};
|
||||
|
@ -1,5 +1,6 @@
|
||||
//! Module defining Rhai data types.
|
||||
|
||||
pub mod custom_types;
|
||||
pub mod dynamic;
|
||||
pub mod error;
|
||||
pub mod fn_ptr;
|
||||
@ -8,6 +9,7 @@ pub mod interner;
|
||||
pub mod parse_error;
|
||||
pub mod scope;
|
||||
|
||||
pub use custom_types::{CustomType, CustomTypesCollection};
|
||||
pub use dynamic::Dynamic;
|
||||
#[cfg(not(feature = "no_std"))]
|
||||
pub use dynamic::Instant;
|
||||
|
@ -53,9 +53,13 @@ fn test_module_sub_module() -> Result<(), Box<EvalAltResult>> {
|
||||
|
||||
assert_eq!(m2.get_var_value::<INT>("answer").unwrap(), 41);
|
||||
|
||||
module.set_custom_type::<()>("Don't Panic");
|
||||
|
||||
let mut engine = Engine::new();
|
||||
engine.register_static_module("question", module.into());
|
||||
|
||||
assert_eq!(engine.eval::<String>("type_of(())")?, "Don't Panic");
|
||||
|
||||
assert_eq!(engine.eval::<INT>("question::MYSTIC_NUMBER")?, 42);
|
||||
assert!(engine.eval::<INT>("MYSTIC_NUMBER").is_err());
|
||||
assert_eq!(engine.eval::<INT>("question::life::universe::answer")?, 41);
|
||||
|
Loading…
Reference in New Issue
Block a user