This repository has been archived by the owner on Nov 15, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 2.6k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
12 changed files
with
358 additions
and
28 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
use proc_macro2::{TokenStream, TokenTree, Group, Span}; | ||
use syn::spanned::Spanned; | ||
use std::iter::once; | ||
|
||
pub fn expand_after(input: proc_macro::TokenStream) -> proc_macro::TokenStream { | ||
let def = syn::parse_macro_input!(input as ExpandAfterDef); | ||
let expand_in_span = def.expand_in.span(); | ||
|
||
match expand_in_stream(&def.expand_after, &mut Some(def.expand_with), def.expand_in) { | ||
Ok(stream) => stream.into(), | ||
Err(_) => { | ||
let msg = format!("Cannot find pattern `{:?}` in given token stream", def.expand_after); | ||
syn::Error::new(expand_in_span, msg).to_compile_error().into() | ||
}, | ||
} | ||
} | ||
|
||
struct ExpandAfterDef { | ||
// This is ensured to have no TokenTree::Group nor TokenTree::Literal and not being empty. | ||
expand_after: Vec<TokenTree>, | ||
expand_with: TokenStream, | ||
expand_in: TokenStream, | ||
} | ||
|
||
impl syn::parse::Parse for ExpandAfterDef { | ||
fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { | ||
let expand_after; | ||
let _replace_with_bracket: syn::token::Brace = syn::braced!(expand_after in input); | ||
let expand_after = expand_after.parse::<TokenStream>()? | ||
.into_iter().collect::<Vec<TokenTree>>(); | ||
|
||
if let Some(t) = expand_after.iter().find(|t| matches!(t, TokenTree::Group(_))) { | ||
return Err(syn::Error::new(t.span(), "Unexpected group token tree")); | ||
} | ||
if let Some(t) = expand_after.iter().find(|t| matches!(t, TokenTree::Literal(_))) { | ||
return Err(syn::Error::new(t.span(), "Unexpected literal token tree")); | ||
} | ||
|
||
if expand_after.is_empty() { | ||
return Err(syn::Error::new(Span::call_site(), "empty match pattern is invalid")); | ||
} | ||
|
||
let expand_with; | ||
let _replace_with_bracket: syn::token::Brace = syn::braced!(expand_with in input); | ||
let expand_with: TokenStream = expand_with.parse()?; | ||
|
||
Ok(Self { | ||
expand_with, | ||
expand_after, | ||
expand_in: input.parse()?, | ||
}) | ||
} | ||
} | ||
|
||
// Replace the first found `auto` ident by content of `with`. | ||
// `with` must be some (Option is used for internal simplification). | ||
// `after` musn't be empty and only contains Ident or Punct | ||
fn expand_in_stream( | ||
after: &Vec<TokenTree>, | ||
with: &mut Option<TokenStream>, | ||
stream: TokenStream | ||
) -> Result<TokenStream, ()> { | ||
assert!(with.is_some(), "`with` must be some, Option is used because `with` is used only once"); | ||
assert!(!after.is_empty(), "`after` mustn't be empty, otherwise it cannot be found"); | ||
|
||
let mut stream = stream.into_iter(); | ||
let mut extended = TokenStream::new(); | ||
let mut match_cursor = 0; | ||
|
||
loop { | ||
match stream.next() { | ||
Some(TokenTree::Group(group)) => { | ||
match_cursor = 0; | ||
let stream = group.stream(); | ||
match expand_in_stream(after, with, stream) { | ||
Ok(stream) => { | ||
extended.extend(once(TokenTree::Group(Group::new(group.delimiter(), stream)))); | ||
break; | ||
} | ||
Err(_) => { | ||
extended.extend(once(TokenTree::Group(group))); | ||
} | ||
} | ||
} | ||
Some(other) => { | ||
use TokenTree::{Ident, Punct}; | ||
|
||
let other_match_pattern = match (&other, &after[match_cursor]) { | ||
(Ident(i1), Ident(i2)) if i1 == i2 => true, | ||
(Punct(p1), Punct(p2)) if p1.as_char() == p2.as_char() => true, | ||
_ => false, | ||
}; | ||
|
||
if other_match_pattern { | ||
match_cursor += 1 | ||
} | ||
|
||
extended.extend(once(other)); | ||
|
||
if match_cursor == after.len() { | ||
extended.extend(once(with.take().expect("with is used to replace only once"))); | ||
break; | ||
} | ||
} | ||
None => return Err(()) | ||
} | ||
} | ||
|
||
extended.extend(stream); | ||
|
||
Ok(extended) | ||
} |
Oops, something went wrong.