Skip to content

Commit

Permalink
Support macros (invocations + macro_rules!)
Browse files Browse the repository at this point in the history
So far limited to mod, impl, trait blocks.
  • Loading branch information
Bromeon committed Jan 23, 2024
1 parent 616c664 commit 573103b
Show file tree
Hide file tree
Showing 11 changed files with 634 additions and 16 deletions.
15 changes: 10 additions & 5 deletions src/parse.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
use crate::error::Error;
use crate::parse_fn::consume_macro;
use crate::parse_impl::{
consume_either_fn_type_const_static_impl, parse_const_or_static, parse_impl, parse_trait,
};
Expand Down Expand Up @@ -218,13 +219,17 @@ pub fn consume_declaration(tokens: &mut Peekable<IntoIter>) -> Result<Declaratio
consume_either_fn_type_const_static_impl(tokens, attributes, vis_marker, "declaration")
}
Some(token) => {
panic!(
"cannot parse declaration: expected keyword struct/enum/union/type/trait/impl/mod/default/const/async/unsafe/extern/fn/static, found token {:?}",
token
);
if let Some(macro_) = consume_macro(tokens, attributes) {
Declaration::Macro(macro_)
} else {
panic!(
"cannot parse declaration: expected keyword struct/enum/union/type/trait/impl/mod/default/const/async/unsafe/extern/fn/static or macro, found token {:?}",
token
);
}
}
None => {
panic!("cannot parse type: expected keyword struct/enum/union/type/trait/impl/mod/default/const/async/unsafe/extern/fn/static, found end-of-stream");
panic!("cannot parse type: expected keyword struct/enum/union/type/trait/impl/mod/default/const/async/unsafe/extern/fn/static or macro, found end-of-stream");
}
};
Ok(declaration)
Expand Down
58 changes: 51 additions & 7 deletions src/parse_fn.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@ use crate::parse_type::{
consume_declaration_name, consume_field_type, consume_generic_params, consume_where_clause,
};
use crate::parse_utils::{
consume_comma, consume_ident, consume_outer_attributes, consume_punct, consume_stuff_until,
parse_any_ident, parse_punct,
consume_any_ident, consume_comma, consume_ident, consume_outer_attributes, consume_punct,
consume_stuff_until, parse_any_ident, parse_punct,
};
use crate::punctuated::Punctuated;
use crate::types::{
FnParam, FnQualifiers, FnReceiverParam, FnTypedParam, Function, GroupSpan, TyExpr,
};
use crate::{Attribute, VisMarker};
use crate::{Attribute, Macro, VisMarker};
use proc_macro2::{Delimiter, Ident, Punct, TokenStream, TokenTree};
use std::iter::Peekable;

Expand Down Expand Up @@ -118,7 +118,7 @@ fn consume_fn_return(tokens: &mut TokenIter) -> Option<([Punct; 2], TyExpr)> {
Some((
[dash, tip],
TyExpr {
tokens: (consume_stuff_until(
tokens: consume_stuff_until(
tokens,
|token| match token {
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => true,
Expand All @@ -127,7 +127,7 @@ fn consume_fn_return(tokens: &mut TokenIter) -> Option<([Punct; 2], TyExpr)> {
_ => false,
},
true,
)),
),
},
))
}
Expand Down Expand Up @@ -187,7 +187,7 @@ pub(crate) fn consume_fn(
TokenTree::Group(group) if group.delimiter() == Delimiter::Parenthesis => {
(parse_fn_params(group.stream()), GroupSpan::new(&group))
}
_ => panic!("cannot parse function"),
_ => panic!("cannot parse function; missing parameter list"),
};

let (tk_return_arrow, return_ty) = if let Some((arrow, ty)) = consume_fn_return(tokens) {
Expand All @@ -203,7 +203,7 @@ pub(crate) fn consume_fn(
(Some(group.clone()), None)
}
TokenTree::Punct(punct) if punct.as_char() == ';' => (None, Some(punct.clone())),
_ => panic!("cannot parse function"),
_ => panic!("cannot parse function; missing body or `;`"),
};

Ok(Function {
Expand All @@ -222,3 +222,47 @@ pub(crate) fn consume_fn(
body: function_body,
})
}

pub(crate) fn consume_macro(tokens: &mut TokenIter, attributes: Vec<Attribute>) -> Option<Macro> {
// TODO consider multiple-lookahead instead of potentially cloning many tokens
let before_start = tokens.clone();

match consume_macro_inner(tokens, attributes) {
Some(macro_) => Some(macro_),
None => {
// rollback iterator, could be start of const declaration
*tokens = before_start;
None
}
}
}

fn consume_macro_inner(tokens: &mut TokenIter, attributes: Vec<Attribute>) -> Option<Macro> {
let name = consume_any_ident(tokens)?;
let tk_bang = consume_punct(tokens, '!')?;
let tk_declared_name = consume_any_ident(tokens);

let (is_paren, macro_body) = match tokens.next().expect("unexpected end of macro") {
TokenTree::Group(group) if group.delimiter() == Delimiter::Parenthesis => (true, group),
TokenTree::Group(group) if group.delimiter() == Delimiter::Brace => (false, group),
_ => panic!("cannot parse macro; missing `{{}}` or `()` group"),
};

let inner_tokens = macro_body.stream().into_iter().collect();

let tk_semicolon = if is_paren {
Some(parse_punct(tokens, ';', "macro invocation semicolon"))
} else {
None
};

Some(Macro {
attributes,
name,
tk_bang,
tk_declared_name,
tk_braces_or_parens: GroupSpan::new(&macro_body),
inner_tokens,
tk_semicolon,
})
}
18 changes: 16 additions & 2 deletions src/parse_impl.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
use crate::parse_fn::{consume_fn, NotFunction};
use crate::parse_fn::{consume_fn, consume_macro, NotFunction};
use crate::parse_mod::parse_mod;
use crate::parse_type::{consume_bound, consume_generic_params, consume_where_clause};
use crate::parse_utils::{
Expand All @@ -9,6 +9,7 @@ use crate::types::{Constant, ImplMember, TyDefinition, ValueExpr};
use crate::types_edition::GroupSpan;
use crate::{Attribute, Declaration, Impl, Trait, TraitMember, TyExpr, VisMarker};
use proc_macro2::{Delimiter, Group, TokenTree};
use quote::ToTokens;
use std::iter::Peekable;

type TokenIter = Peekable<proc_macro2::token_stream::IntoIter>;
Expand Down Expand Up @@ -149,7 +150,18 @@ pub(crate) fn consume_either_fn_type_const_static_impl(
}
}
}
_ => panic!("unsupported {} item `{}`", context, ident),
ident => {
if let Some(vis_marker) = vis_marker {
panic!(
"unsupported visibility marker `{}`",
vis_marker.to_token_stream().to_string()
);
}
match consume_macro(tokens, attributes.clone()) {
Some(macro_) => Declaration::Macro(macro_),
None => panic!("unsupported {} item `{}`", context, ident),
}
}
}
} else {
panic!("unsupported {} element: {:?}", context, tokens.peek())
Expand Down Expand Up @@ -177,6 +189,7 @@ pub(crate) fn parse_impl_body(token_group: Group) -> (GroupSpan, Vec<Attribute>,
Declaration::Function(function) => ImplMember::Method(function),
Declaration::Constant(constant) => ImplMember::Constant(constant),
Declaration::TyDefinition(ty_def) => ImplMember::AssocTy(ty_def),
Declaration::Macro(macro_) => ImplMember::Macro(macro_),
_ => panic!("unsupported impl item `{:?}`", tokens.peek()),
};

Expand Down Expand Up @@ -279,6 +292,7 @@ pub(crate) fn parse_trait(
ImplMember::Method(function) => TraitMember::Method(function),
ImplMember::Constant(constant) => TraitMember::Constant(constant),
ImplMember::AssocTy(assoc_ty) => TraitMember::AssocTy(assoc_ty),
ImplMember::Macro(macro_) => TraitMember::Macro(macro_),
})
.collect();

Expand Down
11 changes: 11 additions & 0 deletions src/parse_utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,17 @@ pub(crate) fn parse_ident(tokens: &mut TokenIter, expected: &str, panic_context:
}
}

pub(crate) fn consume_any_ident(tokens: &mut TokenIter) -> Option<Ident> {
match tokens.peek() {
Some(TokenTree::Ident(ident)) => {
let ident = ident.clone();
tokens.next();
Some(ident)
}
_ => None,
}
}

pub(crate) fn consume_ident(tokens: &mut TokenIter, expected: &str) -> Option<Ident> {
match tokens.peek() {
Some(TokenTree::Ident(ident)) if ident == expected => {
Expand Down
87 changes: 87 additions & 0 deletions src/snapshots/venial__tests__parse_impl_inherent.snap
Original file line number Diff line number Diff line change
Expand Up @@ -260,6 +260,93 @@ Impl(
},
},
),
Macro(
Macro {
attributes: [
Attribute {
tk_hash: Punct {
char: '#',
spacing: Alone,
},
tk_brackets: [],
path: [
clippy,
":",
":",
allow,
],
value: Group(
[
venial,
],
(),
),
},
],
name: Ident(
fn_macro,
),
tk_bang: Punct {
char: '!',
spacing: Alone,
},
tk_declared_name: None,
tk_braces_or_parens: (),
inner_tokens: [
Ident {
sym: MyTrait,
},
],
tk_semicolon: Some(
Punct {
char: ';',
spacing: Alone,
},
),
},
),
Macro(
Macro {
attributes: [],
name: Ident(
block_macro,
),
tk_bang: Punct {
char: '!',
spacing: Alone,
},
tk_declared_name: None,
tk_braces_or_parens: {},
inner_tokens: [
Ident {
sym: fn,
},
Ident {
sym: inner_fn,
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
},
Punct {
char: '-',
spacing: Joint,
},
Punct {
char: '>',
spacing: Alone,
},
Ident {
sym: bool,
},
Punct {
char: ';',
spacing: Alone,
},
],
tk_semicolon: None,
},
),
],
},
)
87 changes: 87 additions & 0 deletions src/snapshots/venial__tests__parse_impl_trait.snap
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,93 @@ Impl(
},
},
),
Macro(
Macro {
attributes: [
Attribute {
tk_hash: Punct {
char: '#',
spacing: Alone,
},
tk_brackets: [],
path: [
clippy,
":",
":",
allow,
],
value: Group(
[
venial,
],
(),
),
},
],
name: Ident(
fn_macro,
),
tk_bang: Punct {
char: '!',
spacing: Alone,
},
tk_declared_name: None,
tk_braces_or_parens: (),
inner_tokens: [
Ident {
sym: MyTrait,
},
],
tk_semicolon: Some(
Punct {
char: ';',
spacing: Alone,
},
),
},
),
Macro(
Macro {
attributes: [],
name: Ident(
block_macro,
),
tk_bang: Punct {
char: '!',
spacing: Alone,
},
tk_declared_name: None,
tk_braces_or_parens: {},
inner_tokens: [
Ident {
sym: fn,
},
Ident {
sym: inner_fn,
},
Group {
delimiter: Parenthesis,
stream: TokenStream [],
},
Punct {
char: '-',
spacing: Joint,
},
Punct {
char: '>',
spacing: Alone,
},
Ident {
sym: bool,
},
Punct {
char: ';',
spacing: Alone,
},
],
tk_semicolon: None,
},
),
],
},
)
Loading

0 comments on commit 573103b

Please sign in to comment.