refactor(downgrade): use bumpalo

This commit is contained in:
2026-02-20 19:05:43 +08:00
parent e1517c338e
commit 53dbee3514
29 changed files with 1087 additions and 985 deletions

View File

@@ -8,5 +8,6 @@ proc-macro = true
[dependencies]
convert_case = "0.11"
proc-macro2 = "1.0"
quote = "1.0"
syn = { version = "2.0", features = ["full"] }

View File

@@ -4,22 +4,22 @@
//! an Intermediate Representation (IR) that follows a specific pattern. It generates:
//! 1. An enum representing the different kinds of IR nodes.
//! 2. Structs for each of the variants that have fields.
//! 3. `Ref` and `Mut` versions of the main enum for ergonomic pattern matching on references.
//! 4. `From` implementations to easily convert from a struct variant (e.g., `BinOp`) to the main enum (`Ir::BinOp`).
//! 5. A `To[IrName]` trait to provide a convenient `.to_ir()` method on the variant structs.
//! 3. `From` implementations to easily convert from a struct variant (e.g., `BinOp`) to the main enum (`Ir::BinOp`).
//! 4. A `To[IrName]` trait to provide a convenient `.to_ir()` method on the variant structs.
use convert_case::{Case, Casing};
use proc_macro::TokenStream;
use quote::{format_ident, quote};
use syn::{
FieldsNamed, Ident, Token, Type, parenthesized,
Expr, ExprPath, FieldsNamed, GenericArgument, GenericParam, Generics, Ident, Path, PathSegment,
Token, Type, TypePath, parenthesized,
parse::{Parse, ParseStream, Result},
punctuated::Punctuated,
token,
};
/// Represents one of the variants passed to the `ir!` macro.
pub enum VariantInput {
enum VariantInput {
/// A unit-like variant, e.g., `Arg`.
Unit(Ident),
/// A tuple-like variant with one unnamed field, e.g., `ExprRef(ExprId)`.
@@ -29,11 +29,12 @@ pub enum VariantInput {
}
/// The top-level input for the `ir!` macro.
pub struct MacroInput {
struct MacroInput {
/// The name of the main IR enum to be generated (e.g., `Ir`).
pub base_name: Ident,
base_name: Ident,
generics: Generics,
/// The list of variants for the enum.
pub variants: Punctuated<VariantInput, Token![,]>,
variants: Punctuated<VariantInput, Token![,]>,
}
impl Parse for VariantInput {
@@ -64,13 +65,14 @@ impl Parse for VariantInput {
impl Parse for MacroInput {
fn parse(input: ParseStream) -> Result<Self> {
// The macro input is expected to be: `IrName, Variant1, Variant2, ...`
let base_name = input.parse()?;
input.parse::<Token![,]>()?;
let generics = Generics::parse(input)?;
input.parse::<Token![;]>()?;
let variants = Punctuated::parse_terminated(input)?;
Ok(MacroInput {
base_name,
generics,
variants,
})
}
@@ -81,17 +83,39 @@ pub fn ir_impl(input: TokenStream) -> TokenStream {
let parsed_input = syn::parse_macro_input!(input as MacroInput);
let base_name = &parsed_input.base_name;
let ref_name = format_ident!("{}Ref", base_name);
let mut_name = format_ident!("{}Mut", base_name);
let generic_params = &parsed_input.generics.params;
let mk_ident_path = |ident| Path {
leading_colon: None,
segments: Punctuated::from_iter(std::iter::once(PathSegment {
ident,
arguments: Default::default(),
})),
};
let generic_args = {
generic_params
.iter()
.map(|arg| match arg {
GenericParam::Lifetime(lifetime) => {
GenericArgument::Lifetime(lifetime.lifetime.clone())
}
GenericParam::Const(cnst) => GenericArgument::Const(Expr::Path(ExprPath {
path: mk_ident_path(cnst.ident.clone()),
attrs: Vec::new(),
qself: None,
})),
GenericParam::Type(ty) => GenericArgument::Type(Type::Path(TypePath {
path: mk_ident_path(ty.ident.clone()),
qself: None,
})),
})
.collect::<Punctuated<_, Token![,]>>()
};
let where_clause = &parsed_input.generics.where_clause;
let to_trait_name = format_ident!("To{}", base_name);
let to_trait_fn_name = format_ident!("to_{}", base_name.to_string().to_case(Case::Snake));
let mut enum_variants = Vec::new();
let mut struct_defs = Vec::new();
let mut ref_variants = Vec::new();
let mut mut_variants = Vec::new();
let mut as_ref_arms = Vec::new();
let mut as_mut_arms = Vec::new();
let mut span_arms = Vec::new();
let mut from_impls = Vec::new();
let mut to_trait_impls = Vec::new();
@@ -109,19 +133,15 @@ pub fn ir_impl(input: TokenStream) -> TokenStream {
});
enum_variants.push(quote! { #name(#inner_type) });
ref_variants.push(quote! { #name(&'a #inner_type) });
mut_variants.push(quote! { #name(&'a mut #inner_type) });
as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) });
as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) });
span_arms.push(quote! { Self::#name(inner) => inner.span });
from_impls.push(quote! {
impl From<#inner_type> for #base_name {
impl <#generic_params> From<#inner_type> for #base_name <#generic_args> #where_clause {
fn from(val: #inner_type) -> Self { #base_name::#name(val) }
}
});
to_trait_impls.push(quote! {
impl #to_trait_name for #name {
fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) }
impl <#generic_params> #to_trait_name <#generic_args> for #name #where_clause {
fn #to_trait_fn_name(self) -> #base_name <#generic_args> { #base_name::from(self) }
}
});
}
@@ -138,25 +158,24 @@ pub fn ir_impl(input: TokenStream) -> TokenStream {
let inner_type = name.clone();
enum_variants.push(quote! { #name(#inner_type) });
ref_variants.push(quote! { #name(&'a #inner_type) });
mut_variants.push(quote! { #name(&'a mut #inner_type) });
as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) });
as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) });
span_arms.push(quote! { Self::#name(inner) => inner.span });
from_impls.push(quote! {
impl From<#inner_type> for #base_name {
impl <#generic_params> From<#inner_type> for #base_name <#generic_args> #where_clause {
fn from(val: #inner_type) -> Self { #base_name::#name(val) }
}
});
to_trait_impls.push(quote! {
impl #to_trait_name for #name {
fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) }
impl <#generic_params> #to_trait_name <#generic_args> for #name #where_clause {
fn #to_trait_fn_name(self) -> #base_name <#generic_args> { #base_name::from(self) }
}
});
}
VariantInput::Struct(name, mut fields) => {
let inner_type = name.clone();
fields.named.iter_mut().for_each(|field| {
field.vis = syn::Visibility::Public(syn::token::Pub::default());
});
fields.named.push(syn::Field {
attrs: vec![],
vis: syn::Visibility::Public(syn::token::Pub::default()),
@@ -168,22 +187,18 @@ pub fn ir_impl(input: TokenStream) -> TokenStream {
struct_defs.push(quote! {
#[derive(Debug)]
pub struct #name #fields
pub struct #name <#generic_params> #where_clause #fields
});
enum_variants.push(quote! { #name(#inner_type) });
ref_variants.push(quote! { #name(&'a #inner_type) });
mut_variants.push(quote! { #name(&'a mut #inner_type) });
as_ref_arms.push(quote! { Self::#name(inner) => #ref_name::#name(inner) });
as_mut_arms.push(quote! { Self::#name(inner) => #mut_name::#name(inner) });
enum_variants.push(quote! { #name(#inner_type <#generic_args>) });
span_arms.push(quote! { Self::#name(inner) => inner.span });
from_impls.push(quote! {
impl From<#inner_type> for #base_name {
fn from(val: #inner_type) -> Self { #base_name::#name(val) }
impl <#generic_params> From<#inner_type <#generic_args>> for #base_name <#generic_args> #where_clause {
fn from(val: #inner_type <#generic_args>) -> Self { #base_name::#name(val) }
}
});
to_trait_impls.push(quote! {
impl #to_trait_name for #name {
fn #to_trait_fn_name(self) -> #base_name { #base_name::from(self) }
impl <#generic_params> #to_trait_name <#generic_args> for #name <#generic_args> #where_clause {
fn #to_trait_fn_name(self) -> #base_name <#generic_args> { #base_name::from(self) }
}
});
}
@@ -193,41 +208,15 @@ pub fn ir_impl(input: TokenStream) -> TokenStream {
// Assemble the final generated code.
let expanded = quote! {
/// The main IR enum, generated by the `ir!` macro.
#[derive(Debug, IsVariant, Unwrap, TryUnwrap)]
pub enum #base_name {
#[derive(Debug)]
pub enum #base_name <#generic_params> #where_clause {
#( #enum_variants ),*
}
// The struct definitions for the enum variants.
#( #struct_defs )*
/// An immutable reference version of the IR enum.
#[derive(Debug, IsVariant, Unwrap, TryUnwrap)]
pub enum #ref_name<'a> {
#( #ref_variants ),*
}
/// A mutable reference version of the IR enum.
#[derive(Debug, IsVariant, Unwrap, TryUnwrap)]
pub enum #mut_name<'a> {
#( #mut_variants ),*
}
impl #base_name {
/// Converts a `&Ir` into a `IrRef`.
pub fn as_ref(&self) -> #ref_name<'_> {
match self {
#( #as_ref_arms ),*
}
}
/// Converts a `&mut Ir` into a `IrMut`.
pub fn as_mut(&mut self) -> #mut_name<'_> {
match self {
#( #as_mut_arms ),*
}
}
impl <#generic_params> #base_name <#generic_args> #where_clause {
pub fn span(&self) -> rnix::TextRange {
match self {
#( #span_arms ),*
@@ -239,9 +228,9 @@ pub fn ir_impl(input: TokenStream) -> TokenStream {
#( #from_impls )*
/// A trait for converting a variant struct into the main IR enum.
pub trait #to_trait_name {
pub trait #to_trait_name <#generic_params> #where_clause {
/// Performs the conversion.
fn #to_trait_fn_name(self) -> #base_name;
fn #to_trait_fn_name(self) -> #base_name <#generic_args>;
}
// Implement the `ToIr` trait for each variant struct.