#![cfg_attr(feature = "nightly", feature(proc_macro_span))]
use proc_macro::Delimiter;
use proc_macro::Group;
use proc_macro::Ident;
use proc_macro::TokenStream;
use proc_macro::TokenTree;
mod parsing;
use parsing::*;
#[proc_macro_attribute]
pub fn rules_derive(attr: TokenStream, item: TokenStream) -> TokenStream {
let mut result = vec![
punct('#'),
brackets([
ident("derive"),
parens([
punct_joint(':'),
punct(':'),
ident("rules_derive"),
punct_joint(':'),
punct(':'),
ident("__RulesDerive"),
]),
]),
punct('#'),
brackets([ident("__rules_derive"), parens(attr)]),
];
result.extend(item);
result.into_iter().collect()
}
#[doc(hidden)]
#[proc_macro_derive(__RulesDerive, attributes(__rules_derive))]
pub fn rules_derive_impl(item: TokenStream) -> TokenStream {
render_macro_result(rules_derive_inner(item))
}
fn rules_derive_inner(item: TokenStream) -> Result<TokenStream> {
let mut item = ParseState::new(item.into_iter());
let mut description = Vec::new();
let attr: TokenStream;
{
let mut attrs = Vec::new();
attr = parse_item_attributes(&mut item, &mut attrs)?;
description.push(parens(attrs));
}
parse_visibility(&mut item, &mut description);
let TokenTree::Ident(tystyle_ident) = item.next()? else {
return item.error(&"expected `struct` or `enum` for #[rules_derive]");
};
enum TyStyle {
Struct,
Enum,
}
let tystyle = match tystyle_ident.to_string().as_str() {
"struct" => TyStyle::Struct,
"enum" => TyStyle::Enum,
"union" => {
return Err(Msg(
tystyle_ident.span(),
&"`union` is not supported by #[rules_derive]",
))
}
_ => {
return Err(Msg(
tystyle_ident.span(),
&"expected `struct` or `enum` for #[rules_derive]",
))
}
};
description.push(TokenTree::Ident(tystyle_ident));
let type_name = item.next()?;
description.push(type_name.clone());
let (ty, generics_bindings, generics_inner) = parse_generics(&mut item, &type_name)?;
let mut generics_where = Vec::new();
parse_where_clause(&mut item, &mut generics_where)?;
let mut variants = Vec::new();
match item.next()? {
TokenTree::Group(group) => {
match group.delimiter() {
proc_macro::Delimiter::Brace => match tystyle {
TyStyle::Struct => {
let fields = parse_named_fields(group.stream())?;
variants.push(type_name.clone());
variants.push(parens([
with_span(group.span_open(), ident("named")),
type_name.clone(),
]));
variants.push(braces(fields));
}
TyStyle::Enum => {
let mut input_variants = ParseState::new(group.stream().into_iter());
while !input_variants.at_end() {
skip_outer_attributes(&mut input_variants)?;
let variant_name = input_variants.next()?; let (variant_metadata, fields) =
if let Some(TokenTree::Group(_)) = &input_variants.peeks[0] {
let TokenTree::Group(enum_item) = input_variants.must_next() else {
unreachable!()
};
match enum_item.delimiter() {
proc_macro::Delimiter::Parenthesis => {
let variant_metadata = parens([
with_span(enum_item.span(), ident("unnamed")),
type_name.clone(),
punct_joint(':'),
punct(':'),
variant_name.clone(),
]);
let fields = braces(parse_unnamed_fields(enum_item.stream())?);
(variant_metadata, fields)
}
proc_macro::Delimiter::Brace => {
let variant_metadata = parens([
with_span(enum_item.span(), ident("named")),
type_name.clone(),
punct_joint(':'),
punct(':'),
variant_name.clone(),
]);
let fields = braces(parse_named_fields(enum_item.stream())?);
(variant_metadata, fields)
}
_ => {
return input_variants.error(&"Parse error");
}
}
} else {
let variant_metadata = parens([
with_span(input_variants.span(), ident("unit")),
type_name.clone(),
punct_joint(':'),
punct(':'),
variant_name.clone(),
]);
let fields = braces([]);
(variant_metadata, fields)
};
let mut discriminant = Vec::new();
if input_variants.peek_punct(0, b"=") {
input_variants.copy(1, &mut discriminant)?; let mut inner_discriminant = Vec::new();
parse_expression(&mut input_variants, &mut inner_discriminant)?;
discriminant.push(parens(inner_discriminant));
}
variants.push(variant_name);
variants.push(variant_metadata);
variants.extend(discriminant);
variants.push(fields);
if input_variants.peek_punct(0, b",") {
input_variants.next().unwrap(); }
}
}
},
proc_macro::Delimiter::Parenthesis => {
variants.push(type_name.clone()); variants.push(parens([
with_span(group.span_open(), ident("unnamed")),
type_name.clone(),
]));
variants.push(braces(parse_unnamed_fields(group.stream())?));
parse_where_clause(&mut item, &mut generics_where)?;
item.next()?; }
_ => {
return item.error(&"Parse error");
}
}
}
TokenTree::Punct(punct) => {
if punct.as_char() != ';' {
return item.error(&"Parse error");
}
variants.push(type_name.clone()); variants.push(parens([
with_span(punct.span(), ident("unit")),
type_name.clone(),
]));
variants.push(braces([]));
}
_ => return item.error(&"Parse error"),
}
description.push(parens([
parens(ty),
parens(generics_bindings),
parens(generics_inner),
ident("where"),
parens(generics_where),
]));
description.push(braces(variants));
let mut derives = Vec::new();
let mut attr = ParseState::new(attr.into_iter());
while !attr.at_end() {
let mut extra_args = None;
let mut is_empty = true;
while let Ok(t) = attr.next() {
derives.push(t);
is_empty = false;
if let Some(TokenTree::Group(_)) = &attr.peeks[0] {
let Ok(TokenTree::Group(group)) = attr.next() else {
unreachable!()
};
extra_args = Some(group.stream());
break;
}
if attr.at_end() || attr.peek_punct(0, b",") {
break;
}
}
if attr.peek_punct(0, b",") {
attr.next()?; }
if is_empty {
break;
}
derives.push(punct('!'));
let mut description_with_args = Vec::new();
if let Some(extra_args) = extra_args {
description_with_args.push(braces(extra_args));
}
description_with_args.extend(description.clone());
derives.push(parens(description_with_args));
derives.push(punct(';'));
}
Ok(derives.into_iter().collect())
}
#[proc_macro]
pub fn make_ident(item: TokenStream) -> TokenStream { render_macro_result(make_ident_inner(item)) }
fn make_ident_inner(item: TokenStream) -> Result<TokenStream> {
let mut name = String::new();
for t in item {
match t {
TokenTree::Ident(ident) => {
name.push_str(&ident.to_string());
}
TokenTree::Literal(literal) => {
name.push_str(&literal.to_string());
}
_ => {
return Err(Msg(
t.span(),
&"make_ident! only accepts idents and literals",
));
}
}
}
Ok(TokenTree::Ident(Ident::new(&name, proc_macro::Span::call_site())).into())
}
#[proc_macro]
pub fn with_spans(item: TokenStream) -> TokenStream {
let mut result = Vec::new();
let result = process_stream(None, ParseState::new(item.into_iter()), &mut result)
.map(|()| result.into_iter().collect());
render_macro_result(result)
}
fn process_stream(
span: Option<proc_macro::Span>,
mut stream: ParseState,
dst: &mut Vec<TokenTree>,
) -> Result<()> {
'token_loop: while !stream.at_end() {
if stream.peek_ident(0, "spanned") && stream.peek_punct(1, b"!") && stream.peek_group(2) {
stream.must_next(); stream.must_next(); if let TokenTree::Group(group) = stream.must_next() {
let mut inner_stream = ParseState::new(group.stream().into_iter());
if let Ok(mut spanned_tt) = inner_stream.next() {
let inner_span;
loop {
if let TokenTree::Group(g) = &spanned_tt {
if matches!(g.delimiter(), Delimiter::None | Delimiter::Brace) {
let mut delimiter_contents = g.stream().into_iter();
let first = delimiter_contents.next();
let last = delimiter_contents.fold(None, |_prev, next| Some(next));
match (first, last) {
(Some(first), Option::None) => {
spanned_tt = first;
continue;
}
(Some(first), Some(last)) => {
#[cfg(feature = "nightly")]
{
inner_span = first
.span()
.join(last.span())
.unwrap_or_else(|| first.span());
}
#[cfg(not(feature = "nightly"))]
{
inner_span = first.span();
_ = last;
}
break;
}
_ => {
}
}
}
}
inner_span = spanned_tt.span();
break;
}
if inner_stream.peek_punct(0, b"=>") {
inner_stream.must_next(); inner_stream.must_next(); process_stream(Some(inner_span), inner_stream, dst)?;
continue 'token_loop;
}
}
}
return stream.error(&"spanned!() must match syntax spanned!($span:tt => ...)");
}
dst.push(process_tree(span, stream.must_next())?);
}
Ok(())
}
fn process_tree(span: Option<proc_macro::Span>, tree: TokenTree) -> Result<TokenTree> {
let mut tree = match tree {
TokenTree::Group(g) => {
let mut inner_dst = Vec::new();
process_stream(
span,
ParseState::new(g.stream().into_iter()),
&mut inner_dst,
)?;
TokenTree::Group(Group::new(g.delimiter(), inner_dst.into_iter().collect()))
}
tree => tree,
};
if let Some(span) = span {
tree.set_span(tree.span().located_at(span));
}
Ok(tree)
}