Refactor macro handling: add pascal_to_snake utility, rewrite Target handling, and update descriptor structure for bits-based targets.
This commit is contained in:
@@ -15,6 +15,7 @@ authors = ["Dean J. Karstedt <dk@cl.team>"]
|
|||||||
license-file = "LICENSE"
|
license-file = "LICENSE"
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
|
pmp-macro = { path = "crates/pmp-macro" }
|
||||||
pmp-emitter = { path = "crates/pmp-emitter" }
|
pmp-emitter = { path = "crates/pmp-emitter" }
|
||||||
|
|
||||||
clap = { version = "^4", features = ["derive"] }
|
clap = { version = "^4", features = ["derive"] }
|
||||||
|
|||||||
@@ -24,3 +24,26 @@ pub fn snake_to_title(s: &str) -> String
|
|||||||
.collect::<Vec<_>>()
|
.collect::<Vec<_>>()
|
||||||
.join(" ")
|
.join(" ")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn pascal_to_snake(s: &str) -> String
|
||||||
|
{
|
||||||
|
let mut out = String::with_capacity(s.len());
|
||||||
|
let mut prev_lower = false;
|
||||||
|
|
||||||
|
for c in s.chars()
|
||||||
|
{
|
||||||
|
if c.is_uppercase()
|
||||||
|
{
|
||||||
|
if prev_lower { out.push('_'); }
|
||||||
|
for lc in c.to_lowercase() { out.push(lc); }
|
||||||
|
prev_lower = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
prev_lower = true;
|
||||||
|
out.push(c);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
out
|
||||||
|
}
|
||||||
|
|||||||
@@ -3,6 +3,22 @@ use proc_macro2::TokenStream as TokenStream2;
|
|||||||
|
|
||||||
use crate::attrs::MacroInput;
|
use crate::attrs::MacroInput;
|
||||||
|
|
||||||
|
fn rewrite_bitor_target_expr(expr: syn::Expr) -> syn::Expr
|
||||||
|
{
|
||||||
|
match expr
|
||||||
|
{
|
||||||
|
syn::Expr::Binary(mut bin) if matches!(bin.op, syn::BinOp::BitOr(_)) => {
|
||||||
|
bin.left = Box::new(rewrite_bitor_target_expr(*bin.left));
|
||||||
|
bin.right = Box::new(rewrite_bitor_target_expr(*bin.right));
|
||||||
|
syn::Expr::Binary(bin)
|
||||||
|
}
|
||||||
|
|
||||||
|
other => {
|
||||||
|
syn::parse_quote! { (#other).bits() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
|
pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
|
||||||
{
|
{
|
||||||
let struct_ident = &parsed.ident;
|
let struct_ident = &parsed.ident;
|
||||||
@@ -16,8 +32,8 @@ pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
|
|||||||
.unwrap_or_else(|| struct_ident.to_string());
|
.unwrap_or_else(|| struct_ident.to_string());
|
||||||
|
|
||||||
let target_expr: syn::Expr = parsed.target
|
let target_expr: syn::Expr = parsed.target
|
||||||
.map(|e| e.0)
|
.map(|e| rewrite_bitor_target_expr(e.0))
|
||||||
.unwrap_or_else(|| syn::parse_quote! { ::pmp_macro_misc::Target::ALL });
|
.unwrap_or_else(|| syn::parse_quote! { ::pmp_macro::Target::ALL.bits() });
|
||||||
|
|
||||||
let docs: String = parsed.docs.map(|s| s.0).unwrap_or_default();
|
let docs: String = parsed.docs.map(|s| s.0).unwrap_or_default();
|
||||||
|
|
||||||
@@ -53,7 +69,7 @@ pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
|
|||||||
};
|
};
|
||||||
|
|
||||||
param_tokens.push(quote_spanned! { span =>
|
param_tokens.push(quote_spanned! { span =>
|
||||||
::pmp_macro_misc::ParamDescriptor {
|
::pmp_macro::ParamDescriptor {
|
||||||
name: #field_name,
|
name: #field_name,
|
||||||
php_type: #php_type,
|
php_type: #php_type,
|
||||||
required: #required,
|
required: #required,
|
||||||
@@ -64,47 +80,48 @@ pub fn expand(parsed: MacroInput) -> syn::Result<TokenStream2>
|
|||||||
|
|
||||||
if let Some(e) = errors { return Err(e); }
|
if let Some(e) = errors { return Err(e); }
|
||||||
|
|
||||||
let mod_ident = format_ident!("__pmp_macro_impl_{}", struct_ident);
|
let snaked_ident = pmp_core::pascal_to_snake(struct_ident.to_string().as_str());
|
||||||
let handler = quote! { super::#struct_ident };
|
let mod_ident = format_ident!("__pmp_macro_impl_{}", snaked_ident);
|
||||||
|
let handler = quote! { super::#struct_ident };
|
||||||
|
|
||||||
Ok(quote!
|
Ok(quote!
|
||||||
{
|
{
|
||||||
#[doc(hidden)]
|
#[doc(hidden)]
|
||||||
mod #mod_ident
|
mod #mod_ident
|
||||||
{
|
{
|
||||||
use ::pmp_macro_misc::{
|
use ::pmp_macro::{
|
||||||
AnalyzeFn, MacroDescriptor, MacroRegistration, ParamDescriptor,
|
AnalyzeFn, MacroDescriptor, MacroRegistration,
|
||||||
TransformFn, PmpMacro,
|
ParamDescriptor, Target, TransformFn, PmpMacro,
|
||||||
};
|
};
|
||||||
|
|
||||||
static PARAMS: &[ParamDescriptor] = &[ #(#param_tokens),* ];
|
static PARAMS: &[ParamDescriptor] = &[ #(#param_tokens),* ];
|
||||||
|
|
||||||
static DESCRIPTOR: MacroDescriptor = MacroDescriptor {
|
static DESCRIPTOR: MacroDescriptor = MacroDescriptor {
|
||||||
name: #attr_name,
|
name: #attr_name,
|
||||||
target: #target_expr,
|
target_bits: #target_expr,
|
||||||
docs: #docs,
|
docs: #docs,
|
||||||
params: PARAMS,
|
params: PARAMS,
|
||||||
};
|
};
|
||||||
|
|
||||||
fn analyze(
|
fn analyze(
|
||||||
ctx: &::pmp_macro_misc::MacroContext,
|
ctx: &::pmp_macro::MacroContext,
|
||||||
attr: &::pmp_macro_misc::ResolvedAttr,
|
attr: &::pmp_macro::ResolvedAttr,
|
||||||
node: &::pmp_macro_misc::AttributedNode,
|
node: &::pmp_macro::AttributedNode,
|
||||||
) -> ::std::vec::Vec<::pmp_macro_misc::Diagnostic>
|
) -> ::std::vec::Vec<::pmp_macro::Diagnostic>
|
||||||
{
|
{
|
||||||
<#handler as PmpMacro>::analyze(&::std::default::Default::default(), ctx, attr, node)
|
<#handler as PmpMacro>::analyze(&::std::default::Default::default(), ctx, attr, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn transform(
|
fn transform(
|
||||||
ctx: &::pmp_macro_misc::MacroContext,
|
ctx: &::pmp_macro::MacroContext,
|
||||||
attr: &::pmp_macro_misc::ResolvedAttr,
|
attr: &::pmp_macro::ResolvedAttr,
|
||||||
node: &::pmp_macro_misc::AttributedNode,
|
node: &::pmp_macro::AttributedNode,
|
||||||
) -> ::std::vec::Vec<::pmp_macro_misc::Rewrite>
|
) -> ::std::vec::Vec<::pmp_macro::Rewrite>
|
||||||
{
|
{
|
||||||
<#handler as PmpMacro>::transform(&::std::default::Default::default(), ctx, attr, node)
|
<#handler as PmpMacro>::transform(&::std::default::Default::default(), ctx, attr, node)
|
||||||
}
|
}
|
||||||
|
|
||||||
::inventory::submit! {
|
::pmp_macro::inventory::submit! {
|
||||||
MacroRegistration::new(&DESCRIPTOR, analyze, transform)
|
MacroRegistration::new(&DESCRIPTOR, analyze, transform)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,36 +1,26 @@
|
|||||||
use crate::target::Target;
|
use crate::target::Target;
|
||||||
|
|
||||||
/// Describes a single constructor parameter of a PHP macro attribute.
|
|
||||||
///
|
|
||||||
/// All fields are `&'static str` so the descriptor can be embedded as a
|
|
||||||
/// `static` and referenced from the inventory without allocation.
|
|
||||||
#[derive(Debug, Clone, Copy)]
|
#[derive(Debug, Clone, Copy)]
|
||||||
pub struct ParamDescriptor {
|
pub struct ParamDescriptor {
|
||||||
/// The parameter name as it appears in the PHP constructor.
|
|
||||||
pub name: &'static str,
|
pub name: &'static str,
|
||||||
/// A human-readable PHP type hint, e.g. `"class-string[]"` or `"bool"`.
|
|
||||||
pub php_type: &'static str,
|
pub php_type: &'static str,
|
||||||
/// Whether the parameter must be supplied by the caller.
|
|
||||||
pub required: bool,
|
pub required: bool,
|
||||||
/// The PHP source representation of the default value, e.g. `"[]"` or `"false"`.
|
|
||||||
/// `None` when `required` is `true`.
|
|
||||||
pub default: Option<&'static str>,
|
pub default: Option<&'static str>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Static metadata for a registered PHP macro attribute.
|
|
||||||
///
|
|
||||||
/// Generated by `#[derive(PmpMacro)]` and stored as a `&'static MacroDescriptor`
|
|
||||||
/// in the inventory. Entirely allocation-free.
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct MacroDescriptor {
|
pub struct MacroDescriptor {
|
||||||
/// The PHP attribute name, e.g. `"Sealed"`. Used to match against
|
pub name: &'static str,
|
||||||
/// attribute usages in parsed PHP files.
|
/// Raw bitflags value - use `.target()` to get a `Target`.
|
||||||
pub name: &'static str,
|
/// Stored as `u16` so the static initializer remains `const`.
|
||||||
/// Which PHP constructs this attribute is permitted to annotate.
|
pub target_bits: u16,
|
||||||
pub target: Target,
|
pub docs: &'static str,
|
||||||
/// Human-readable description of what this macro does.
|
pub params: &'static [ParamDescriptor],
|
||||||
/// Used for error messages and generated documentation.
|
}
|
||||||
pub docs: &'static str,
|
|
||||||
/// The constructor parameters this attribute accepts.
|
impl MacroDescriptor
|
||||||
pub params: &'static [ParamDescriptor],
|
{
|
||||||
|
pub fn target(&self) -> Target {
|
||||||
|
Target::from_bits_truncate(self.target_bits)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,7 +14,10 @@ mod traits;
|
|||||||
mod context;
|
mod context;
|
||||||
mod descriptor;
|
mod descriptor;
|
||||||
|
|
||||||
pub use target::Target;
|
#[doc(hidden)]
|
||||||
pub use descriptor::{ MacroDescriptor, ParamDescriptor };
|
pub use inventory;
|
||||||
pub use traits::{ PmpMacro, MacroRegistry, MacroRegistration, };
|
|
||||||
pub use context::{ Rewrite, ResolvedAttr, Diagnostic, MacroContext, AttributedNode };
|
pub use target::*;
|
||||||
|
pub use traits::*;
|
||||||
|
pub use context::*;
|
||||||
|
pub use descriptor::*;
|
||||||
|
|||||||
Reference in New Issue
Block a user