peepmatic: Introduce the peepmatic-macro crate
This crate provides the derive macros used by `peepmatic`, notable AST-related derives that enumerate child AST nodes, and operator-related derives that provide helpers for type checking.
This commit is contained in:
15
cranelift/peepmatic/crates/macro/Cargo.toml
Normal file
15
cranelift/peepmatic/crates/macro/Cargo.toml
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
[package]
|
||||||
|
name = "peepmatic-macro"
|
||||||
|
version = "0.1.0"
|
||||||
|
authors = ["Nick Fitzgerald <fitzgen@gmail.com>"]
|
||||||
|
edition = "2018"
|
||||||
|
|
||||||
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
proc-macro2 = "1.0.9"
|
||||||
|
quote = "1.0.3"
|
||||||
|
syn = { version = "1.0.16", features = ['extra-traits'] }
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
proc_macro = true
|
||||||
110
cranelift/peepmatic/crates/macro/src/child_nodes.rs
Normal file
110
cranelift/peepmatic/crates/macro/src/child_nodes.rs
Normal file
@@ -0,0 +1,110 @@
|
|||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::{parse_quote, GenericParam, Generics, Result};
|
||||||
|
|
||||||
|
pub fn derive_child_nodes(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||||
|
let children = get_child_nodes(&input.data)?;
|
||||||
|
let name = &input.ident;
|
||||||
|
let generics = add_trait_bounds(input.generics.clone());
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl #impl_generics ChildNodes<'a, 'a> for #name #ty_generics #where_clause {
|
||||||
|
fn child_nodes(&'a self, children: &mut impl Extend<DynAstRef<'a>>) {
|
||||||
|
#children
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_child_nodes(data: &syn::Data) -> Result<impl quote::ToTokens> {
|
||||||
|
match data {
|
||||||
|
syn::Data::Struct(s) => {
|
||||||
|
let mut fields = vec![];
|
||||||
|
|
||||||
|
match &s.fields {
|
||||||
|
syn::Fields::Named(n) => {
|
||||||
|
for f in n.named.iter() {
|
||||||
|
let opts = crate::PeepmaticOpts::from_attrs(&mut f.attrs.clone())?;
|
||||||
|
|
||||||
|
if opts.skip_child {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let field_name = f.ident.as_ref().unwrap();
|
||||||
|
if opts.flatten {
|
||||||
|
fields.push(quote! {
|
||||||
|
self.#field_name.iter().map(DynAstRef::from)
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
fields.push(quote! {
|
||||||
|
std::iter::once(DynAstRef::from(&self.#field_name))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Fields::Unnamed(u) => {
|
||||||
|
for (i, f) in u.unnamed.iter().enumerate() {
|
||||||
|
let opts = crate::PeepmaticOpts::from_attrs(&mut f.attrs.clone())?;
|
||||||
|
if opts.skip_child {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if opts.flatten {
|
||||||
|
return Err(syn::Error::new(
|
||||||
|
u.paren_token.span,
|
||||||
|
"#[peepmatic(flatten)] is only allowed with named fields",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
fields.push(quote! {
|
||||||
|
std::iter::once(DynAstRef::from(&self.#i))
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Fields::Unit => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(match fields.as_slice() {
|
||||||
|
[] => quote! { let _ = children; },
|
||||||
|
[f, rest @ ..] => {
|
||||||
|
let rest = rest.iter().map(|f| {
|
||||||
|
quote! {
|
||||||
|
.chain(#f)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
children.extend( #f #( #rest )* );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
syn::Data::Enum(e) => {
|
||||||
|
let mut match_arms = vec![];
|
||||||
|
for v in e.variants.iter() {
|
||||||
|
match v.fields {
|
||||||
|
syn::Fields::Unnamed(ref u) if u.unnamed.len() == 1 => {
|
||||||
|
let variant = &v.ident;
|
||||||
|
match_arms.push(quote! {
|
||||||
|
Self::#variant(x) => children.extend(Some(x.into())),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
_ => panic!("#[derive(ChildNodes)] only supports enums whose variants all ahve a single unnamed field")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Ok(quote! {
|
||||||
|
match self {
|
||||||
|
#( #match_arms )*
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
syn::Data::Union(_) => panic!("#[derive(ChildNodes)] is not supported on unions"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn add_trait_bounds(mut generics: Generics) -> Generics {
|
||||||
|
for param in &mut generics.params {
|
||||||
|
if let GenericParam::Type(type_param) = param {
|
||||||
|
type_param.bounds.push(parse_quote!(ChildNodes<'a, 'a>));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generics
|
||||||
|
}
|
||||||
23
cranelift/peepmatic/crates/macro/src/into_dyn_ast_ref.rs
Normal file
23
cranelift/peepmatic/crates/macro/src/into_dyn_ast_ref.rs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::Result;
|
||||||
|
|
||||||
|
pub fn derive_into_dyn_ast_ref(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||||
|
let ty = &input.ident;
|
||||||
|
|
||||||
|
let opts = crate::PeepmaticOpts::from_attrs(&mut input.attrs.clone())?;
|
||||||
|
if opts.no_into_dyn_node {
|
||||||
|
return Ok(quote! {});
|
||||||
|
}
|
||||||
|
|
||||||
|
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl #impl_generics From<&'a #ty #ty_generics> for DynAstRef<'a> #where_clause {
|
||||||
|
#[inline]
|
||||||
|
fn from(x: &'a #ty #ty_generics) -> Self {
|
||||||
|
Self::#ty(x)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
156
cranelift/peepmatic/crates/macro/src/lib.rs
Normal file
156
cranelift/peepmatic/crates/macro/src/lib.rs
Normal file
@@ -0,0 +1,156 @@
|
|||||||
|
extern crate proc_macro;
|
||||||
|
|
||||||
|
use crate::proc_macro::TokenStream;
|
||||||
|
use proc_macro2::Span;
|
||||||
|
use quote::quote;
|
||||||
|
use syn::parse::{Parse, ParseStream};
|
||||||
|
use syn::punctuated::Punctuated;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::Error;
|
||||||
|
use syn::{parse_macro_input, Ident, Result};
|
||||||
|
|
||||||
|
mod child_nodes;
|
||||||
|
mod into_dyn_ast_ref;
|
||||||
|
mod operator;
|
||||||
|
mod span;
|
||||||
|
|
||||||
|
#[proc_macro_derive(PeepmaticOperator, attributes(peepmatic))]
|
||||||
|
pub fn operator(input: TokenStream) -> TokenStream {
|
||||||
|
operator::derive_operator(input)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[proc_macro_derive(Ast, attributes(peepmatic))]
|
||||||
|
pub fn derive_ast(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let span_impl = match span::derive_span(&input) {
|
||||||
|
Ok(s) => s,
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let child_nodes_impl = match child_nodes::derive_child_nodes(&input) {
|
||||||
|
Ok(c) => c,
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let into_dyn_ast_ref_impl = match into_dyn_ast_ref::derive_into_dyn_ast_ref(&input) {
|
||||||
|
Ok(n) => n,
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
#span_impl
|
||||||
|
#child_nodes_impl
|
||||||
|
#into_dyn_ast_ref_impl
|
||||||
|
};
|
||||||
|
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Default)]
|
||||||
|
pub(crate) struct PeepmaticOpts {
|
||||||
|
// `ChildNodes` options.
|
||||||
|
skip_child: bool,
|
||||||
|
flatten: bool,
|
||||||
|
|
||||||
|
// `From<&'a Self> for DynAstRef<'a>` options.
|
||||||
|
no_into_dyn_node: bool,
|
||||||
|
|
||||||
|
// Peepmatic operator options.
|
||||||
|
immediates_paren: syn::token::Paren,
|
||||||
|
immediates: Vec<syn::Ident>,
|
||||||
|
params_paren: syn::token::Paren,
|
||||||
|
params: Vec<syn::Ident>,
|
||||||
|
result: Option<syn::Ident>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Parse for PeepmaticOpts {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
enum Attr {
|
||||||
|
Immediates(syn::token::Paren, Vec<syn::Ident>),
|
||||||
|
Params(syn::token::Paren, Vec<syn::Ident>),
|
||||||
|
Result(syn::Ident),
|
||||||
|
NoIntoDynNode,
|
||||||
|
SkipChild,
|
||||||
|
Flatten,
|
||||||
|
}
|
||||||
|
|
||||||
|
let attrs = Punctuated::<_, syn::token::Comma>::parse_terminated(input)?;
|
||||||
|
let mut ret = PeepmaticOpts::default();
|
||||||
|
for attr in attrs {
|
||||||
|
match attr {
|
||||||
|
Attr::Immediates(paren, imms) => {
|
||||||
|
ret.immediates_paren = paren;
|
||||||
|
ret.immediates = imms;
|
||||||
|
}
|
||||||
|
Attr::Params(paren, ps) => {
|
||||||
|
ret.params_paren = paren;
|
||||||
|
ret.params = ps;
|
||||||
|
}
|
||||||
|
Attr::Result(r) => ret.result = Some(r),
|
||||||
|
Attr::NoIntoDynNode => ret.no_into_dyn_node = true,
|
||||||
|
Attr::SkipChild => ret.skip_child = true,
|
||||||
|
Attr::Flatten => ret.flatten = true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Ok(ret);
|
||||||
|
|
||||||
|
impl Parse for Attr {
|
||||||
|
fn parse(input: ParseStream) -> Result<Self> {
|
||||||
|
let attr: Ident = input.parse()?;
|
||||||
|
if attr == "immediates" {
|
||||||
|
let inner;
|
||||||
|
let paren = syn::parenthesized!(inner in input);
|
||||||
|
let imms = Punctuated::<_, syn::token::Comma>::parse_terminated(&inner)?;
|
||||||
|
return Ok(Attr::Immediates(paren, imms.into_iter().collect()));
|
||||||
|
}
|
||||||
|
if attr == "params" {
|
||||||
|
let inner;
|
||||||
|
let paren = syn::parenthesized!(inner in input);
|
||||||
|
let params = Punctuated::<_, syn::token::Comma>::parse_terminated(&inner)?;
|
||||||
|
return Ok(Attr::Params(paren, params.into_iter().collect()));
|
||||||
|
}
|
||||||
|
if attr == "result" {
|
||||||
|
let inner;
|
||||||
|
syn::parenthesized!(inner in input);
|
||||||
|
return Ok(Attr::Result(syn::Ident::parse(&inner)?));
|
||||||
|
}
|
||||||
|
if attr == "skip_child" {
|
||||||
|
return Ok(Attr::SkipChild);
|
||||||
|
}
|
||||||
|
if attr == "no_into_dyn_node" {
|
||||||
|
return Ok(Attr::NoIntoDynNode);
|
||||||
|
}
|
||||||
|
if attr == "flatten" {
|
||||||
|
return Ok(Attr::Flatten);
|
||||||
|
}
|
||||||
|
return Err(Error::new(attr.span(), "unexpected attribute"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn peepmatic_attrs(attrs: &mut Vec<syn::Attribute>) -> TokenStream {
|
||||||
|
let mut ret = proc_macro2::TokenStream::new();
|
||||||
|
let ident = syn::Path::from(syn::Ident::new("peepmatic", Span::call_site()));
|
||||||
|
for i in (0..attrs.len()).rev() {
|
||||||
|
if attrs[i].path != ident {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
let attr = attrs.remove(i);
|
||||||
|
let group = match attr.tokens.into_iter().next().unwrap() {
|
||||||
|
proc_macro2::TokenTree::Group(g) => g,
|
||||||
|
_ => panic!("#[peepmatic(...)] expected"),
|
||||||
|
};
|
||||||
|
ret.extend(group.stream());
|
||||||
|
ret.extend(quote! { , });
|
||||||
|
}
|
||||||
|
return ret.into();
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PeepmaticOpts {
|
||||||
|
pub(crate) fn from_attrs(attrs: &mut Vec<syn::Attribute>) -> syn::Result<Self> {
|
||||||
|
syn::parse(peepmatic_attrs(attrs))
|
||||||
|
}
|
||||||
|
}
|
||||||
325
cranelift/peepmatic/crates/macro/src/operator.rs
Normal file
325
cranelift/peepmatic/crates/macro/src/operator.rs
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
//! Implementation of the `#[peepmatic]` macro for the `Operator` AST node.
|
||||||
|
|
||||||
|
use crate::proc_macro::TokenStream;
|
||||||
|
use crate::PeepmaticOpts;
|
||||||
|
use proc_macro2::{Ident, Span};
|
||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::Error;
|
||||||
|
use syn::{parse_macro_input, Result};
|
||||||
|
|
||||||
|
pub fn derive_operator(input: TokenStream) -> TokenStream {
|
||||||
|
let input = parse_macro_input!(input as DeriveInput);
|
||||||
|
|
||||||
|
let variants = match get_enum_variants(&input) {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let arity = match create_arity(&variants) {
|
||||||
|
Ok(a) => a,
|
||||||
|
Err(e) => return e.to_compile_error().into(),
|
||||||
|
};
|
||||||
|
|
||||||
|
let num_operators = variants.len();
|
||||||
|
let type_methods = create_type_methods(&variants);
|
||||||
|
let parse_impl = create_parse_impl(&input.ident, &variants);
|
||||||
|
let display_impl = create_display_impl(&input.ident, &variants);
|
||||||
|
let try_from_u32_impl = create_try_from_u32_impl(&input.ident, &variants);
|
||||||
|
let ident = &input.ident;
|
||||||
|
|
||||||
|
let expanded = quote! {
|
||||||
|
impl #ident {
|
||||||
|
#arity
|
||||||
|
#type_methods
|
||||||
|
|
||||||
|
/// Get the total number of different operators.
|
||||||
|
pub const fn num_operators() -> usize {
|
||||||
|
#num_operators
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#display_impl
|
||||||
|
#try_from_u32_impl
|
||||||
|
#parse_impl
|
||||||
|
};
|
||||||
|
|
||||||
|
// eprintln!("{}", expanded);
|
||||||
|
TokenStream::from(expanded)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn get_enum_variants(input: &DeriveInput) -> Result<Vec<OperatorVariant>> {
|
||||||
|
let en = match &input.data {
|
||||||
|
syn::Data::Enum(en) => en,
|
||||||
|
syn::Data::Struct(_) => {
|
||||||
|
panic!("can only put #[peepmatic] on an enum; found it on a struct")
|
||||||
|
}
|
||||||
|
syn::Data::Union(_) => panic!("can only put #[peepmatic] on an enum; found it on a union"),
|
||||||
|
};
|
||||||
|
en.variants
|
||||||
|
.iter()
|
||||||
|
.cloned()
|
||||||
|
.map(|mut variant| {
|
||||||
|
Ok(OperatorVariant {
|
||||||
|
opts: PeepmaticOpts::from_attrs(&mut variant.attrs)?,
|
||||||
|
syn: variant,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
struct OperatorVariant {
|
||||||
|
syn: syn::Variant,
|
||||||
|
opts: PeepmaticOpts,
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_arity(variants: &[OperatorVariant]) -> Result<impl quote::ToTokens> {
|
||||||
|
let mut imm_arities = vec![];
|
||||||
|
let mut params_arities = vec![];
|
||||||
|
|
||||||
|
for v in variants {
|
||||||
|
let variant = &v.syn.ident;
|
||||||
|
|
||||||
|
let imm_arity = v.opts.immediates.len();
|
||||||
|
if imm_arity > std::u8::MAX as usize {
|
||||||
|
return Err(Error::new(
|
||||||
|
v.opts.immediates_paren.span,
|
||||||
|
"cannot have more than u8::MAX immediates",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let imm_arity = imm_arity as u8;
|
||||||
|
|
||||||
|
imm_arities.push(quote! {
|
||||||
|
Self::#variant => #imm_arity,
|
||||||
|
});
|
||||||
|
|
||||||
|
let params_arity = v.opts.params.len();
|
||||||
|
if params_arity > std::u8::MAX as usize {
|
||||||
|
return Err(Error::new(
|
||||||
|
v.opts.params_paren.span,
|
||||||
|
"cannot have more than u8::MAX params",
|
||||||
|
));
|
||||||
|
}
|
||||||
|
let params_arity = params_arity as u8;
|
||||||
|
|
||||||
|
params_arities.push(quote! {
|
||||||
|
Self::#variant => #params_arity,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
/// Get the number of immediates that this operator has.
|
||||||
|
pub fn immediates_arity(&self) -> u8 {
|
||||||
|
match *self {
|
||||||
|
#( #imm_arities )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the number of parameters that this operator takes.
|
||||||
|
pub fn params_arity(&self) -> u8 {
|
||||||
|
match *self {
|
||||||
|
#( #params_arities )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_type_methods(variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||||
|
let mut result_types = vec![];
|
||||||
|
let mut imm_types = vec![];
|
||||||
|
let mut param_types = vec![];
|
||||||
|
|
||||||
|
for v in variants {
|
||||||
|
let variant = &v.syn.ident;
|
||||||
|
|
||||||
|
let result_ty = v.opts.result.as_ref().unwrap_or_else(|| {
|
||||||
|
panic!(
|
||||||
|
"must define #[peepmatic(result(..))] on operator `{}`",
|
||||||
|
variant
|
||||||
|
)
|
||||||
|
});
|
||||||
|
result_types.push(quote! {
|
||||||
|
Self::#variant => {
|
||||||
|
context.#result_ty(span)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let imm_tys = match &v.opts.immediates[..] {
|
||||||
|
[] => quote! {},
|
||||||
|
[ty, rest @ ..] => {
|
||||||
|
let rest = rest.iter().map(|ty| {
|
||||||
|
quote! { .chain(::std::iter::once(context.#ty(span))) }
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
imm_types.push(quote! {
|
||||||
|
Self::#variant => {
|
||||||
|
#imm_tys
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let param_tys = match &v.opts.params[..] {
|
||||||
|
[] => quote! {},
|
||||||
|
[ty, rest @ ..] => {
|
||||||
|
let rest = rest.iter().map(|ty| {
|
||||||
|
quote! { .chain(::std::iter::once(context.#ty(span))) }
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
types.extend(::std::iter::once(context.#ty(span))#( #rest )*);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
param_types.push(quote! {
|
||||||
|
Self::#variant => {
|
||||||
|
#param_tys
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
/// Get the result type of this operator.
|
||||||
|
#[cfg(feature = "construct")]
|
||||||
|
pub fn result_type<'a, C>(
|
||||||
|
&self,
|
||||||
|
context: &mut C,
|
||||||
|
span: wast::Span,
|
||||||
|
) -> C::TypeVariable
|
||||||
|
where
|
||||||
|
C: 'a + TypingContext<'a>,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
#( #result_types )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the immediate types of this operator.
|
||||||
|
#[cfg(feature = "construct")]
|
||||||
|
pub fn immediate_types<'a, C>(
|
||||||
|
&self,
|
||||||
|
context: &mut C,
|
||||||
|
span: wast::Span,
|
||||||
|
types: &mut impl Extend<C::TypeVariable>,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
C: 'a + TypingContext<'a>,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
#( #imm_types )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Get the parameter types of this operator.
|
||||||
|
#[cfg(feature = "construct")]
|
||||||
|
pub fn param_types<'a, C>(
|
||||||
|
&self,
|
||||||
|
context: &mut C,
|
||||||
|
span: wast::Span,
|
||||||
|
types: &mut impl Extend<C::TypeVariable>,
|
||||||
|
)
|
||||||
|
where
|
||||||
|
C: 'a + TypingContext<'a>,
|
||||||
|
{
|
||||||
|
match *self {
|
||||||
|
#( #param_types )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn snake_case(s: &str) -> String {
|
||||||
|
let mut t = String::with_capacity(s.len() + 1);
|
||||||
|
for (i, ch) in s.chars().enumerate() {
|
||||||
|
if i != 0 && ch.is_uppercase() {
|
||||||
|
t.push('_');
|
||||||
|
}
|
||||||
|
t.extend(ch.to_lowercase());
|
||||||
|
}
|
||||||
|
t
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_parse_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||||
|
let token_defs = variants.iter().map(|v| {
|
||||||
|
let tok = snake_case(&v.syn.ident.to_string());
|
||||||
|
let tok = Ident::new(&tok, Span::call_site());
|
||||||
|
quote! {
|
||||||
|
wast::custom_keyword!(#tok);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let parses = variants.iter().map(|v| {
|
||||||
|
let tok = snake_case(&v.syn.ident.to_string());
|
||||||
|
let tok = Ident::new(&tok, Span::call_site());
|
||||||
|
let ident = &v.syn.ident;
|
||||||
|
quote! {
|
||||||
|
if p.peek::<#tok>() {
|
||||||
|
p.parse::<#tok>()?;
|
||||||
|
return Ok(Self::#ident);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let expected = format!("expected {}", ident);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
#[cfg(feature = "construct")]
|
||||||
|
impl<'a> wast::parser::Parse<'a> for #ident {
|
||||||
|
fn parse(p: wast::parser::Parser<'a>) -> wast::parser::Result<Self> {
|
||||||
|
#( #token_defs )*
|
||||||
|
|
||||||
|
#( #parses )*
|
||||||
|
|
||||||
|
Err(p.error(#expected))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_display_impl(ident: &syn::Ident, variants: &[OperatorVariant]) -> impl quote::ToTokens {
|
||||||
|
let displays = variants.iter().map(|v| {
|
||||||
|
let variant = &v.syn.ident;
|
||||||
|
let snake = snake_case(&v.syn.ident.to_string());
|
||||||
|
quote! {
|
||||||
|
Self::#variant => write!(f, #snake),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl std::fmt::Display for #ident {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
|
||||||
|
match self {
|
||||||
|
#( #displays )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn create_try_from_u32_impl(
|
||||||
|
ident: &syn::Ident,
|
||||||
|
variants: &[OperatorVariant],
|
||||||
|
) -> impl quote::ToTokens {
|
||||||
|
let matches = variants.iter().map(|v| {
|
||||||
|
let variant = &v.syn.ident;
|
||||||
|
quote! {
|
||||||
|
x if Self::#variant as u32 == x => Ok(Self::#variant),
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
let error_msg = format!("value is not an `{}`", ident);
|
||||||
|
|
||||||
|
quote! {
|
||||||
|
impl std::convert::TryFrom<u32> for #ident {
|
||||||
|
type Error = &'static str;
|
||||||
|
|
||||||
|
fn try_from(value: u32) -> Result<Self, Self::Error> {
|
||||||
|
match value {
|
||||||
|
#( #matches )*
|
||||||
|
_ => Err(#error_msg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
50
cranelift/peepmatic/crates/macro/src/span.rs
Normal file
50
cranelift/peepmatic/crates/macro/src/span.rs
Normal file
@@ -0,0 +1,50 @@
|
|||||||
|
use quote::quote;
|
||||||
|
use syn::DeriveInput;
|
||||||
|
use syn::{parse_quote, GenericParam, Generics, Result};
|
||||||
|
|
||||||
|
pub fn derive_span(input: &DeriveInput) -> Result<impl quote::ToTokens> {
|
||||||
|
let ty = &input.ident;
|
||||||
|
|
||||||
|
let body = match &input.data {
|
||||||
|
syn::Data::Struct(_) => quote! { self.span },
|
||||||
|
syn::Data::Enum(e) => {
|
||||||
|
let variants = e.variants.iter().map(|v| match v.fields {
|
||||||
|
syn::Fields::Unnamed(ref fields) if fields.unnamed.len() == 1 => {
|
||||||
|
let variant = &v.ident;
|
||||||
|
quote! { #ty::#variant(x) => x.span(), }
|
||||||
|
}
|
||||||
|
_ => panic!(
|
||||||
|
"derive(Ast) on enums only supports variants with a single, unnamed field"
|
||||||
|
),
|
||||||
|
});
|
||||||
|
quote! {
|
||||||
|
match self {
|
||||||
|
#( #variants )*
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
syn::Data::Union(_) => panic!("derive(Ast) can only be used with structs and enums, not unions"),
|
||||||
|
};
|
||||||
|
|
||||||
|
let generics = add_span_trait_bounds(input.generics.clone());
|
||||||
|
let (impl_generics, ty_generics, where_clause) = generics.split_for_impl();
|
||||||
|
|
||||||
|
Ok(quote! {
|
||||||
|
impl #impl_generics Span for #ty #ty_generics #where_clause {
|
||||||
|
#[inline]
|
||||||
|
fn span(&self) -> wast::Span {
|
||||||
|
#body
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add a bound `T: Span` to every type parameter `T`.
|
||||||
|
fn add_span_trait_bounds(mut generics: Generics) -> Generics {
|
||||||
|
for param in &mut generics.params {
|
||||||
|
if let GenericParam::Type(ref mut type_param) = *param {
|
||||||
|
type_param.bounds.push(parse_quote!(Span));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
generics
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user