use self::private::WithSpan;
#[cfg(feature = "parsing")]
use crate::buffer::Cursor;
#[cfg(feature = "parsing")]
use crate::error::Result;
#[cfg(feature = "parsing")]
use crate::lifetime::Lifetime;
#[cfg(feature = "parsing")]
use crate::lit::{Lit, LitBool, LitByte, LitByteStr, LitChar, LitFloat, LitInt, LitStr};
#[cfg(feature = "parsing")]
use crate::lookahead;
#[cfg(feature = "parsing")]
use crate::parse::{Parse, ParseStream};
use crate::span::IntoSpans;
#[cfg(any(feature = "parsing", feature = "printing"))]
use proc_macro2::Ident;
use proc_macro2::Span;
#[cfg(feature = "printing")]
use proc_macro2::TokenStream;
#[cfg(feature = "parsing")]
use proc_macro2::{Delimiter, Literal, Punct, TokenTree};
#[cfg(feature = "printing")]
use quote::{ToTokens, TokenStreamExt};
#[cfg(feature = "extra-traits")]
use std::cmp;
#[cfg(feature = "extra-traits")]
use std::fmt::{self, Debug};
#[cfg(feature = "extra-traits")]
use std::hash::{Hash, Hasher};
use std::ops::{Deref, DerefMut};
#[cfg(feature = "parsing")]
pub trait Token: private::Sealed {
#[doc(hidden)]
fn peek(cursor: Cursor) -> bool;
#[doc(hidden)]
fn display() -> &'static str;
}
mod private {
use proc_macro2::Span;
#[cfg(feature = "parsing")]
pub trait Sealed {}
#[repr(C)]
pub struct WithSpan {
pub span: Span,
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for Ident {}
#[cfg(feature = "parsing")]
fn peek_impl(cursor: Cursor, peek: fn(ParseStream) -> bool) -> bool {
use crate::parse::Unexpected;
use std::cell::Cell;
use std::rc::Rc;
let scope = Span::call_site();
let unexpected = Rc::new(Cell::new(Unexpected::None));
let buffer = crate::parse::new_parse_buffer(scope, cursor, unexpected);
peek(&buffer)
}
macro_rules! impl_token {
($display:tt $name:ty) => {
#[cfg(feature = "parsing")]
impl Token for $name {
fn peek(cursor: Cursor) -> bool {
fn peek(input: ParseStream) -> bool {
<$name as Parse>::parse(input).is_ok()
}
peek_impl(cursor, peek)
}
fn display() -> &'static str {
$display
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for $name {}
};
}
impl_token!("lifetime" Lifetime);
impl_token!("literal" Lit);
impl_token!("string literal" LitStr);
impl_token!("byte string literal" LitByteStr);
impl_token!("byte literal" LitByte);
impl_token!("character literal" LitChar);
impl_token!("integer literal" LitInt);
impl_token!("floating point literal" LitFloat);
impl_token!("boolean literal" LitBool);
impl_token!("group token" proc_macro2::Group);
macro_rules! impl_low_level_token {
($display:tt $ty:ident $get:ident) => {
#[cfg(feature = "parsing")]
impl Token for $ty {
fn peek(cursor: Cursor) -> bool {
cursor.$get().is_some()
}
fn display() -> &'static str {
$display
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for $ty {}
};
}
impl_low_level_token!("punctuation token" Punct punct);
impl_low_level_token!("literal" Literal literal);
impl_low_level_token!("token" TokenTree token_tree);
#[doc(hidden)]
#[cfg(feature = "parsing")]
pub trait CustomToken {
fn peek(cursor: Cursor) -> bool;
fn display() -> &'static str;
}
#[cfg(feature = "parsing")]
impl<T: CustomToken> private::Sealed for T {}
#[cfg(feature = "parsing")]
impl<T: CustomToken> Token for T {
fn peek(cursor: Cursor) -> bool {
<Self as CustomToken>::peek(cursor)
}
fn display() -> &'static str {
<Self as CustomToken>::display()
}
}
macro_rules! define_keywords {
($($token:tt pub struct $name:ident #[$doc:meta])*) => {
$(
#[$doc]
pub struct $name {
pub span: Span,
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn $name<S: IntoSpans<[Span; 1]>>(span: S) -> $name {
$name {
span: span.into_spans()[0],
}
}
impl std::default::Default for $name {
fn default() -> Self {
$name {
span: Span::call_site(),
}
}
}
#[cfg(feature = "clone-impls")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
impl Copy for $name {}
#[cfg(feature = "clone-impls")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
impl Clone for $name {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(stringify!($name))
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl cmp::Eq for $name {}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl PartialEq for $name {
fn eq(&self, _other: &$name) -> bool {
true
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Hash for $name {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
#[cfg(feature = "printing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for $name {
fn to_tokens(&self, tokens: &mut TokenStream) {
printing::keyword($token, self.span, tokens);
}
}
#[cfg(feature = "parsing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for $name {
fn parse(input: ParseStream) -> Result<Self> {
Ok($name {
span: parsing::keyword(input, $token)?,
})
}
}
#[cfg(feature = "parsing")]
impl Token for $name {
fn peek(cursor: Cursor) -> bool {
parsing::peek_keyword(cursor, $token)
}
fn display() -> &'static str {
concat!("`", $token, "`")
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for $name {}
)*
};
}
macro_rules! impl_deref_if_len_is_1 {
($name:ident/1) => {
impl Deref for $name {
type Target = WithSpan;
fn deref(&self) -> &Self::Target {
unsafe { &*(self as *const Self as *const WithSpan) }
}
}
impl DerefMut for $name {
fn deref_mut(&mut self) -> &mut Self::Target {
unsafe { &mut *(self as *mut Self as *mut WithSpan) }
}
}
};
($name:ident/$len:tt) => {};
}
macro_rules! define_punctuation_structs {
($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
$(
#[repr(C)]
#[$doc]
pub struct $name {
pub spans: [Span; $len],
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn $name<S: IntoSpans<[Span; $len]>>(spans: S) -> $name {
$name {
spans: spans.into_spans(),
}
}
impl std::default::Default for $name {
fn default() -> Self {
$name {
spans: [Span::call_site(); $len],
}
}
}
#[cfg(feature = "clone-impls")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
impl Copy for $name {}
#[cfg(feature = "clone-impls")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
impl Clone for $name {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(stringify!($name))
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl cmp::Eq for $name {}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl PartialEq for $name {
fn eq(&self, _other: &$name) -> bool {
true
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Hash for $name {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl_deref_if_len_is_1!($name/$len);
)*
};
}
macro_rules! define_punctuation {
($($token:tt pub struct $name:ident/$len:tt #[$doc:meta])*) => {
$(
define_punctuation_structs! {
$token pub struct $name/$len #[$doc]
}
#[cfg(feature = "printing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for $name {
fn to_tokens(&self, tokens: &mut TokenStream) {
printing::punct($token, &self.spans, tokens);
}
}
#[cfg(feature = "parsing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for $name {
fn parse(input: ParseStream) -> Result<Self> {
Ok($name {
spans: parsing::punct(input, $token)?,
})
}
}
#[cfg(feature = "parsing")]
impl Token for $name {
fn peek(cursor: Cursor) -> bool {
parsing::peek_punct(cursor, $token)
}
fn display() -> &'static str {
concat!("`", $token, "`")
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for $name {}
)*
};
}
macro_rules! define_delimiters {
($($token:tt pub struct $name:ident #[$doc:meta])*) => {
$(
#[$doc]
pub struct $name {
pub span: Span,
}
#[doc(hidden)]
#[allow(non_snake_case)]
pub fn $name<S: IntoSpans<[Span; 1]>>(span: S) -> $name {
$name {
span: span.into_spans()[0],
}
}
impl std::default::Default for $name {
fn default() -> Self {
$name {
span: Span::call_site(),
}
}
}
#[cfg(feature = "clone-impls")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
impl Copy for $name {}
#[cfg(feature = "clone-impls")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "clone-impls")))]
impl Clone for $name {
fn clone(&self) -> Self {
*self
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Debug for $name {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
f.write_str(stringify!($name))
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl cmp::Eq for $name {}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl PartialEq for $name {
fn eq(&self, _other: &$name) -> bool {
true
}
}
#[cfg(feature = "extra-traits")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "extra-traits")))]
impl Hash for $name {
fn hash<H: Hasher>(&self, _state: &mut H) {}
}
impl $name {
#[cfg(feature = "printing")]
pub fn surround<F>(&self, tokens: &mut TokenStream, f: F)
where
F: FnOnce(&mut TokenStream),
{
printing::delim($token, self.span, tokens, f);
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for $name {}
)*
};
}
define_punctuation_structs! {
"_" pub struct Underscore/1
}
#[cfg(feature = "printing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "printing")))]
impl ToTokens for Underscore {
fn to_tokens(&self, tokens: &mut TokenStream) {
tokens.append(Ident::new("_", self.span));
}
}
#[cfg(feature = "parsing")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "parsing")))]
impl Parse for Underscore {
fn parse(input: ParseStream) -> Result<Self> {
input.step(|cursor| {
if let Some((ident, rest)) = cursor.ident() {
if ident == "_" {
return Ok((Underscore(ident.span()), rest));
}
}
if let Some((punct, rest)) = cursor.punct() {
if punct.as_char() == '_' {
return Ok((Underscore(punct.span()), rest));
}
}
Err(cursor.error("expected `_`"))
})
}
}
#[cfg(feature = "parsing")]
impl Token for Underscore {
fn peek(cursor: Cursor) -> bool {
if let Some((ident, _rest)) = cursor.ident() {
return ident == "_";
}
if let Some((punct, _rest)) = cursor.punct() {
return punct.as_char() == '_';
}
false
}
fn display() -> &'static str {
"`_`"
}
}
#[cfg(feature = "parsing")]
impl private::Sealed for Underscore {}
#[cfg(feature = "parsing")]
impl Token for Paren {
fn peek(cursor: Cursor) -> bool {
lookahead::is_delimiter(cursor, Delimiter::Parenthesis)
}
fn display() -> &'static str {
"parentheses"
}
}
#[cfg(feature = "parsing")]
impl Token for Brace {
fn peek(cursor: Cursor) -> bool {
lookahead::is_delimiter(cursor, Delimiter::Brace)
}
fn display() -> &'static str {
"curly braces"
}
}
#[cfg(feature = "parsing")]
impl Token for Bracket {
fn peek(cursor: Cursor) -> bool {
lookahead::is_delimiter(cursor, Delimiter::Bracket)
}
fn display() -> &'static str {
"square brackets"
}
}
#[cfg(feature = "parsing")]
impl Token for Group {
fn peek(cursor: Cursor) -> bool {
lookahead::is_delimiter(cursor, Delimiter::None)
}
fn display() -> &'static str {
"invisible group"
}
}
define_keywords! {
"abstract" pub struct Abstract
"as" pub struct As
"async" pub struct Async
"auto" pub struct Auto
"await" pub struct Await
"become" pub struct Become
"box" pub struct Box
"break" pub struct Break
"const" pub struct Const
"continue" pub struct Continue
"crate" pub struct Crate
"default" pub struct Default
"do" pub struct Do
"dyn" pub struct Dyn
"else" pub struct Else
"enum" pub struct Enum
"extern" pub struct Extern
"final" pub struct Final
"fn" pub struct Fn
"for" pub struct For
"if" pub struct If
"impl" pub struct Impl
"in" pub struct In
"let" pub struct Let
"loop" pub struct Loop
"macro" pub struct Macro
"match" pub struct Match
"mod" pub struct Mod
"move" pub struct Move
"mut" pub struct Mut
"override" pub struct Override
"priv" pub struct Priv
"pub" pub struct Pub
"ref" pub struct Ref
"return" pub struct Return
"Self" pub struct SelfType
"self" pub struct SelfValue
"static" pub struct Static
"struct" pub struct Struct
"super" pub struct Super
"trait" pub struct Trait
"try" pub struct Try
"type" pub struct Type
"typeof" pub struct Typeof
"union" pub struct Union
"unsafe" pub struct Unsafe
"unsized" pub struct Unsized
"use" pub struct Use
"virtual" pub struct Virtual
"where" pub struct Where
"while" pub struct While
"yield" pub struct Yield
}
define_punctuation! {
"+" pub struct Add/1
"+=" pub struct AddEq/2
"&" pub struct And/1
"&&" pub struct AndAnd/2
"&=" pub struct AndEq/2
"@" pub struct At/1
"!" pub struct Bang/1
"^" pub struct Caret/1
"^=" pub struct CaretEq/2
":" pub struct Colon/1
"::" pub struct Colon2/2
"," pub struct Comma/1
"/" pub struct Div/1
"/=" pub struct DivEq/2
"$" pub struct Dollar/1
"." pub struct Dot/1
".." pub struct Dot2/2
"..." pub struct Dot3/3
"..=" pub struct DotDotEq/3
"=" pub struct Eq/1
"==" pub struct EqEq/2
">=" pub struct Ge/2
">" pub struct Gt/1
"<=" pub struct Le/2
"<" pub struct Lt/1
"*=" pub struct MulEq/2
"!=" pub struct Ne/2
"|" pub struct Or/1
"|=" pub struct OrEq/2
"||" pub struct OrOr/2
"#" pub struct Pound/1
"?" pub struct Question/1
"->" pub struct RArrow/2
"<-" pub struct LArrow/2
"%" pub struct Rem/1
"%=" pub struct RemEq/2
"=>" pub struct FatArrow/2
";" pub struct Semi/1
"<<" pub struct Shl/2
"<<=" pub struct ShlEq/3
">>" pub struct Shr/2
">>=" pub struct ShrEq/3
"*" pub struct Star/1
"-" pub struct Sub/1
"-=" pub struct SubEq/2
"~" pub struct Tilde/1
}
define_delimiters! {
"{" pub struct Brace
"[" pub struct Bracket
"(" pub struct Paren
" " pub struct Group
}
macro_rules! export_token_macro {
($($await_rule:tt)*) => {
#[macro_export]
macro_rules! Token {
[abstract] => { $crate::token::Abstract };
[as] => { $crate::token::As };
[async] => { $crate::token::Async };
[auto] => { $crate::token::Auto };
$($await_rule => { $crate::token::Await };)*
[become] => { $crate::token::Become };
[box] => { $crate::token::Box };
[break] => { $crate::token::Break };
[const] => { $crate::token::Const };
[continue] => { $crate::token::Continue };
[crate] => { $crate::token::Crate };
[default] => { $crate::token::Default };
[do] => { $crate::token::Do };
[dyn] => { $crate::token::Dyn };
[else] => { $crate::token::Else };
[enum] => { $crate::token::Enum };
[extern] => { $crate::token::Extern };
[final] => { $crate::token::Final };
[fn] => { $crate::token::Fn };
[for] => { $crate::token::For };
[if] => { $crate::token::If };
[impl] => { $crate::token::Impl };
[in] => { $crate::token::In };
[let] => { $crate::token::Let };
[loop] => { $crate::token::Loop };
[macro] => { $crate::token::Macro };
[match] => { $crate::token::Match };
[mod] => { $crate::token::Mod };
[move] => { $crate::token::Move };
[mut] => { $crate::token::Mut };
[override] => { $crate::token::Override };
[priv] => { $crate::token::Priv };
[pub] => { $crate::token::Pub };
[ref] => { $crate::token::Ref };
[return] => { $crate::token::Return };
[Self] => { $crate::token::SelfType };
[self] => { $crate::token::SelfValue };
[static] => { $crate::token::Static };
[struct] => { $crate::token::Struct };
[super] => { $crate::token::Super };
[trait] => { $crate::token::Trait };
[try] => { $crate::token::Try };
[type] => { $crate::token::Type };
[typeof] => { $crate::token::Typeof };
[union] => { $crate::token::Union };
[unsafe] => { $crate::token::Unsafe };
[unsized] => { $crate::token::Unsized };
[use] => { $crate::token::Use };
[virtual] => { $crate::token::Virtual };
[where] => { $crate::token::Where };
[while] => { $crate::token::While };
[yield] => { $crate::token::Yield };
[+] => { $crate::token::Add };
[+=] => { $crate::token::AddEq };
[&] => { $crate::token::And };
[&&] => { $crate::token::AndAnd };
[&=] => { $crate::token::AndEq };
[@] => { $crate::token::At };
[!] => { $crate::token::Bang };
[^] => { $crate::token::Caret };
[^=] => { $crate::token::CaretEq };
[:] => { $crate::token::Colon };
[::] => { $crate::token::Colon2 };
[,] => { $crate::token::Comma };
[/] => { $crate::token::Div };
[/=] => { $crate::token::DivEq };
[$] => { $crate::token::Dollar };
[.] => { $crate::token::Dot };
[..] => { $crate::token::Dot2 };
[...] => { $crate::token::Dot3 };
[..=] => { $crate::token::DotDotEq };
[=] => { $crate::token::Eq };
[==] => { $crate::token::EqEq };
[>=] => { $crate::token::Ge };
[>] => { $crate::token::Gt };
[<=] => { $crate::token::Le };
[<] => { $crate::token::Lt };
[*=] => { $crate::token::MulEq };
[!=] => { $crate::token::Ne };
[|] => { $crate::token::Or };
[|=] => { $crate::token::OrEq };
[||] => { $crate::token::OrOr };
[#] => { $crate::token::Pound };
[?] => { $crate::token::Question };
[->] => { $crate::token::RArrow };
[<-] => { $crate::token::LArrow };
[%] => { $crate::token::Rem };
[%=] => { $crate::token::RemEq };
[=>] => { $crate::token::FatArrow };
[;] => { $crate::token::Semi };
[<<] => { $crate::token::Shl };
[<<=] => { $crate::token::ShlEq };
[>>] => { $crate::token::Shr };
[>>=] => { $crate::token::ShrEq };
[*] => { $crate::token::Star };
[-] => { $crate::token::Sub };
[-=] => { $crate::token::SubEq };
[~] => { $crate::token::Tilde };
[_] => { $crate::token::Underscore };
}
};
}
#[cfg(not(syn_omit_await_from_token_macro))]
include!("await.rs");
#[cfg(syn_omit_await_from_token_macro)]
export_token_macro! {}
#[doc(hidden)]
#[cfg(feature = "parsing")]
pub mod parsing {
use crate::buffer::Cursor;
use crate::error::{Error, Result};
use crate::parse::ParseStream;
use crate::span::FromSpans;
use proc_macro2::{Spacing, Span};
pub fn keyword(input: ParseStream, token: &str) -> Result<Span> {
input.step(|cursor| {
if let Some((ident, rest)) = cursor.ident() {
if ident == token {
return Ok((ident.span(), rest));
}
}
Err(cursor.error(format!("expected `{}`", token)))
})
}
pub fn peek_keyword(cursor: Cursor, token: &str) -> bool {
if let Some((ident, _rest)) = cursor.ident() {
ident == token
} else {
false
}
}
pub fn punct<S: FromSpans>(input: ParseStream, token: &str) -> Result<S> {
let mut spans = [input.span(); 3];
punct_helper(input, token, &mut spans)?;
Ok(S::from_spans(&spans))
}
fn punct_helper(input: ParseStream, token: &str, spans: &mut [Span; 3]) -> Result<()> {
input.step(|cursor| {
let mut cursor = *cursor;
assert!(token.len() <= spans.len());
for (i, ch) in token.chars().enumerate() {
match cursor.punct() {
Some((punct, rest)) => {
spans[i] = punct.span();
if punct.as_char() != ch {
break;
} else if i == token.len() - 1 {
return Ok(((), rest));
} else if punct.spacing() != Spacing::Joint {
break;
}
cursor = rest;
}
None => break,
}
}
Err(Error::new(spans[0], format!("expected `{}`", token)))
})
}
pub fn peek_punct(mut cursor: Cursor, token: &str) -> bool {
for (i, ch) in token.chars().enumerate() {
match cursor.punct() {
Some((punct, rest)) => {
if punct.as_char() != ch {
break;
} else if i == token.len() - 1 {
return true;
} else if punct.spacing() != Spacing::Joint {
break;
}
cursor = rest;
}
None => break,
}
}
false
}
}
#[doc(hidden)]
#[cfg(feature = "printing")]
pub mod printing {
use proc_macro2::{Delimiter, Group, Ident, Punct, Spacing, Span, TokenStream};
use quote::TokenStreamExt;
pub fn punct(s: &str, spans: &[Span], tokens: &mut TokenStream) {
assert_eq!(s.len(), spans.len());
let mut chars = s.chars();
let mut spans = spans.iter();
let ch = chars.next_back().unwrap();
let span = spans.next_back().unwrap();
for (ch, span) in chars.zip(spans) {
let mut op = Punct::new(ch, Spacing::Joint);
op.set_span(*span);
tokens.append(op);
}
let mut op = Punct::new(ch, Spacing::Alone);
op.set_span(*span);
tokens.append(op);
}
pub fn keyword(s: &str, span: Span, tokens: &mut TokenStream) {
tokens.append(Ident::new(s, span));
}
pub fn delim<F>(s: &str, span: Span, tokens: &mut TokenStream, f: F)
where
F: FnOnce(&mut TokenStream),
{
let delim = match s {
"(" => Delimiter::Parenthesis,
"[" => Delimiter::Bracket,
"{" => Delimiter::Brace,
" " => Delimiter::None,
_ => panic!("unknown delimiter: {}", s),
};
let mut inner = TokenStream::new();
f(&mut inner);
let mut g = Group::new(delim, inner);
g.set_span(span);
tokens.append(g);
}
}