pubfn add_item_macro(&mutself, input: &ItemMacro) {
assert!(
input.ident.is_some(), "Expected macro_rules! def. Got {input:?}"
); let m = input.mac.parse_body::<MacroDef>(); iflet Ok(mac) = m { let ident = input.ident.clone().unwrap(); self.defs.insert(ident, mac);
}
}
pubfn evaluate_item_macro(&self, input: &ItemMacro) -> Vec<Item> {
assert!(input.ident.is_none(), "Expected macro usage. Got {input:?}"); let m = input.mac.parse_body::<TokenStream>(); iflet Ok(mac) = m { // FIXME: Extremely hacky. In the future for importing macros, we'll want to do something else. let ident = input.mac.path.segments.last().unwrap().ident.clone();
iflet Some(def) = self.defs.get(&ident) {
def.evaluate(mac)
} else {
panic!("Could not find definition for {ident}. Have you tried creating a #[diplomat::macro_rules] macro_rules! {ident} definition?");
}
} else { // We handle errors automatically in `diplomat/macro`
Vec::new()
}
}
pubfn evaluate_impl_item_macro(&self, input: &ImplItemMacro) -> Vec<ImplItem> { let m: syn::Result<TokenStream> = input.mac.parse_body(); // FIXME: Extremely hacky. In the future for importing macros, we'll want to do something else. let path_ident = input.mac.path.segments.last().unwrap().ident.clone();
iflet Ok(matched) = m { iflet Some(def) = self.defs.get(&path_ident) {
def.evaluate(matched)
} else {
panic!("Could not find definition for {path_ident}. Have you tried creating a #[diplomat::macro_rules] macro_rules! {path_ident} definition?");
}
} else { // We handle errors automatically in `diplomat/macro`
Vec::new()
}
}
}
impl Parse for MacroIdent { fn parse(input: parse::ParseStream) -> syn::Result<Self> {
input.parse::<Token![$]>()?; let ident: Ident = input.parse()?;
input.parse::<Token![:]>()?; let ty: Ident = input.parse()?;
Ok(Self { ident, ty })
}
}
/// Hack to read information from the TokenTree while saving the rest to be later copied into a buffer. /// Will attempt to read `T`, and on a success all of the unread tokens after the stream will be written to `remaining`. struct ParsePartial<T: Parse> {
item: T,
remaining: TokenStream,
}
impl<T: Parse> Parse for ParsePartial<T> { fn parse(input: ParseStream) -> syn::Result<Self> { let item = input.parse::<T>()?; let remaining = input.parse()?;
Ok(Self { item, remaining })
}
}
impl<T: Parse> ParsePartial<T> where
MacroFrag: From<T>,
{ fn try_parse(
i: &MacroIdent,
cursor: &Cursor,
args: &mut HashMap<Ident, MacroFrag>,
) -> syn::Result<TokenBuffer> { let out = syn::parse2::<ParsePartial<T>>(cursor.token_stream())?;
let buf = TokenBuffer::new2(out.remaining);
Ok(buf)
}
}
#[derive(Debug)] #[non_exhaustive] /// A site where a macro is used (i.e., `example!(...)`) /// Constructed in [`MacroUse::parse`] (called by [`MacroDef::evaluate`]) when a previously defined macro is used. We use the definition of [`MacroDef`] to then construct the args in the used macro. pubstruct MacroUse { /// The arguments $argname:MacroFragSpec passed to the macro. Indexed by `argname`. Used for substitution during [`MacroDef::evaluate`].
args: HashMap<Ident, MacroFrag>,
}
impl MacroUse { /// Not an official implementation of [`syn::parse::Parse`], since we need to know the [`MacroDef`] to know how to extract [`MacroUse::args`]. fn parse(def: &MacroDef, stream: TokenStream) -> syn::Result<Self> { letmut args = HashMap::new();
#[derive(Debug)] /// A token representing part of a macro's arguments. /// Used for determining how to parse [`MacroUse`]. /// First constructed with [`MacroMatcher::parse`] inside of [`MacroDef::parse`]. Then, we compare with [`MacroUse::parse_macro_matcher`]. pubenum MacroMatch { /// A token, excluding $ or delimeters. See https://doc.rust-lang.org/reference/tokens.html#grammar-Token
Tokens(TokenStream), /// A delimeter-separated vector of [`MacroMatch`].
MacroMatcher(MacroMatcher), /// A $ident:MacroFragSpec pairing.
Ident(MacroIdent), // TODO: $(MacroMatch+) MacroRepSep? MacroRepOp
}
accepted_tokens!(
lookahead,
tokens,
input,
[syn::Ident, syn::Lit, syn::Lifetime], // Not including the Eq symbols (OrEq, AndEq), since they confuse the parser.
[
Eq, Lt, Ne, Ge, Gt, Not, Tilde, Plus, Minus, Star, Slash, Percent, Caret, And, Or,
Shl, Shr, At, Dot, Comma, Semi, Colon, Pound, Question, Underscore
]
);
if !tokens.is_empty() {
Ok(MacroMatch::Tokens(tokens))
} else {
Err(Error::new(
input.span(),
format!("Did not recognize token. {input:?}"),
))
}
}
}
#[derive(Debug)] /// Represents any given macro definition's arguments, and information on how to parse them for use. /// A MacroMatcher is a delimited list of [`MacroMatch`]es. When you call the macro `example!(...)`, the macro matcher is the parentheses delimited tokens: `(...)`. /// Used to compare a [`MacroDef`] against a [`MacroUse`] when parsing arguments. pubstruct MacroMatcher { pub delim: proc_macro2::Delimiter, pub matches: Vec<MacroMatch>,
}
impl Parse for MacroMatcher { fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> { let lookahead = input.lookahead1();
letmut matches = Vec::new();
let delim; let arm;
if lookahead.peek(token::Paren) {
delim = proc_macro2::Delimiter::Parenthesis;
parenthesized!(arm in input);
} elseif lookahead.peek(token::Brace) {
delim = proc_macro2::Delimiter::Brace;
braced!(arm in input);
} elseif lookahead.peek(token::Bracket) {
delim = proc_macro2::Delimiter::Bracket;
bracketed!(arm in input);
} else { return Err(Error::new(input.span(), "Expected {}, (), or []"));
}
while !arm.is_empty() {
matches.push(arm.parse::<MacroMatch>()?);
}
letmut c = inner; whilelet Some((tt, next)) = c.token_tree() { match &tt {
TokenTree::Punct(p) if p.as_char() == '$' => { iflet Some((tt, next)) = next.token_tree() { iflet TokenTree::Ident(i) = tt {
matched.args[&i].to_tokens(&mut stream);
c = next;
} else {
panic!("Expected ident next to $, got {tt:?}");
}
} else {
panic!("Expected token tree.");
}
}
TokenTree::Group(g) => { let (inner, _, next) = c.group(g.delimiter()).unwrap(); let group =
proc_macro2::Group::new(g.delimiter(), Self::parse_group(matched, inner)); // Once we detect a group, we push it to the array for syn to evaluate.
stream.append(group);
c = next;
}
_ => {
stream.append(tt);
c = next
}
}
}
let buf = TokenBuffer::new2(self.body.clone()); letmut c = buf.begin(); // Search until we find a token to replace: whilelet Some((tt, next)) = c.token_tree() { match &tt {
TokenTree::Punct(punct) if punct.as_char() == '$' => { iflet Some((tt, next)) = next.token_tree() { iflet TokenTree::Ident(i) = tt {
matched.args[&i].to_tokens(&mut stream);
c = next;
} else {
panic!("Expected ident next to $, got {tt:?}");
}
} else {
panic!("Expected token tree.");
}
}
TokenTree::Group(g) => { let (inner, _, next) = c.group(g.delimiter()).unwrap(); // We need to read inside of any groups to find and replace `$` idents. let group =
proc_macro2::Group::new(g.delimiter(), Self::parse_group(&matched, inner));
stream.append(group);
c = next;
}
_ => {
stream.append(tt);
c = next
}
}
}
// Now we have a stream to read through. We read through the whole thing and assume each thing we read is a top level item. let maybe_list = syn::parse_str::<ItemList<T>>(&stream.to_string()); iflet Ok(i) = maybe_list {
i.items
} else {
panic!("Macro expansion error: {:?}", maybe_list.unwrap_err());
}
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.