Skip to content

Commit

Permalink
Move generics parsing into helper file
Browse files Browse the repository at this point in the history
  • Loading branch information
y86-dev committed Apr 30, 2023
1 parent 427defa commit 7223d60
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 84 deletions.
85 changes: 85 additions & 0 deletions pinned-init-macro/src/helpers.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use proc_macro2::{Punct, Spacing, TokenStream, TokenTree};

pub(crate) struct Generics {
pub(crate) impl_generics: Vec<TokenTree>,
pub(crate) ty_generics: Vec<TokenTree>,
}

/// Parses the given `TokenStream` into `Generics` and the rest.
///
/// The generics are not present in the rest, but a where clause might remain.
pub(crate) fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
// `impl_generics`, the declared generics with their bounds.
let mut impl_generics = vec![];
// Only the names of the generics, without any bounds.
let mut ty_generics = vec![];
// Tokens not related to the generics e.g. the `where` token and definition.
let mut rest = vec![];
// The current level of `<`.
let mut nesting = 0;
let mut toks = input.into_iter();
// If we are at the beginning of a generic parameter.
let mut at_start = true;
for tt in &mut toks {
match tt.clone() {
TokenTree::Punct(p) if p.as_char() == '<' => {
if nesting >= 1 {
// This is inside of the generics and part of some bound.
impl_generics.push(tt);
}
nesting += 1;
}
TokenTree::Punct(p) if p.as_char() == '>' => {
// This is a parsing error, so we just end it here.
if nesting == 0 {
break;
} else {
nesting -= 1;
if nesting >= 1 {
// We are still inside of the generics and part of some bound.
impl_generics.push(tt);
}
if nesting == 0 {
break;
}
}
}
tt => {
if nesting == 1 {
// Here depending on the token, it might be a generic variable name.
match &tt {
// Ignore const.
TokenTree::Ident(i) if i.to_string() == "const" => {}
TokenTree::Ident(_) if at_start => {
ty_generics.push(tt.clone());
// We also already push the `,` token, this makes it easier to append
// generics.
ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
at_start = false;
}
TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
// Lifetimes begin with `'`.
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
ty_generics.push(tt.clone());
}
_ => {}
}
}
if nesting >= 1 {
impl_generics.push(tt);
} else if nesting == 0 {
// If we haven't entered the generics yet, we still want to keep these tokens.
rest.push(tt);
}
}
}
}
rest.extend(toks);
(
Generics {
impl_generics,
ty_generics,
},
rest,
)
}
1 change: 1 addition & 0 deletions pinned-init-macro/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
mod helpers;
mod pin_data;
mod pinned_drop;

Expand Down
85 changes: 1 addition & 84 deletions pinned-init-macro/src/pin_data.rs
Original file line number Diff line number Diff line change
@@ -1,92 +1,9 @@
// SPDX-License-Identifier: Apache-2.0 OR MIT

use crate::helpers::{parse_generics, Generics};
use proc_macro2::{Group, Punct, Spacing, TokenStream, TokenTree};
use quote::quote;

struct Generics {
impl_generics: Vec<TokenTree>,
ty_generics: Vec<TokenTree>,
}

/// Parses the given `TokenStream` into `Generics` and the rest.
///
/// The generics are not present in the rest, but a where clause might remain.
fn parse_generics(input: TokenStream) -> (Generics, Vec<TokenTree>) {
// `impl_generics`, the declared generics with their bounds.
let mut impl_generics = vec![];
// Only the names of the generics, without any bounds.
let mut ty_generics = vec![];
// Tokens not related to the generics e.g. the `where` token and definition.
let mut rest = vec![];
// The current level of `<`.
let mut nesting = 0;
let mut toks = input.into_iter();
// If we are at the beginning of a generic parameter.
let mut at_start = true;
for tt in &mut toks {
match tt.clone() {
TokenTree::Punct(p) if p.as_char() == '<' => {
if nesting >= 1 {
// This is inside of the generics and part of some bound.
impl_generics.push(tt);
}
nesting += 1;
}
TokenTree::Punct(p) if p.as_char() == '>' => {
// This is a parsing error, so we just end it here.
if nesting == 0 {
break;
} else {
nesting -= 1;
if nesting >= 1 {
// We are still inside of the generics and part of some bound.
impl_generics.push(tt);
}
if nesting == 0 {
break;
}
}
}
tt => {
if nesting == 1 {
// Here depending on the token, it might be a generic variable name.
match &tt {
// Ignore const.
TokenTree::Ident(i) if i.to_string() == "const" => {}
TokenTree::Ident(_) if at_start => {
ty_generics.push(tt.clone());
// We also already push the `,` token, this makes it easier to append
// generics.
ty_generics.push(TokenTree::Punct(Punct::new(',', Spacing::Alone)));
at_start = false;
}
TokenTree::Punct(p) if p.as_char() == ',' => at_start = true,
// Lifetimes begin with `'`.
TokenTree::Punct(p) if p.as_char() == '\'' && at_start => {
ty_generics.push(tt.clone());
}
_ => {}
}
}
if nesting >= 1 {
impl_generics.push(tt);
} else if nesting == 0 {
// If we haven't entered the generics yet, we still want to keep these tokens.
rest.push(tt);
}
}
}
}
rest.extend(toks);
(
Generics {
impl_generics,
ty_generics,
},
rest,
)
}

pub(crate) fn pin_data(
args: proc_macro::TokenStream,
input: proc_macro::TokenStream,
Expand Down

0 comments on commit 7223d60

Please sign in to comment.