logos_codegen/parser/
nested.rs

1use proc_macro2::token_stream::IntoIter as TokenIter;
2use proc_macro2::{Ident, Literal, TokenStream, TokenTree};
3use quote::quote;
4
5use crate::util::{expect_punct, is_punct};
6
7pub enum NestedValue {
8    /// `name = ...`
9    Assign(TokenStream),
10    /// `name "literal"`
11    Literal(Literal),
12    /// `name(...)`
13    Group(TokenStream),
14    /// `name ident = ...`
15    KeywordAssign(Ident, TokenStream),
16}
17
18pub enum Nested {
19    /// Unnamed nested attribute, such as a string,
20    /// callback closure, or a lone ident/path
21    ///
22    /// Note: a lone ident will be Named with no value instead
23    Unnamed(TokenStream),
24    /// Named: name ...
25    Named(Ident, NestedValue),
26    /// Unexpected token,
27    Unexpected(TokenStream),
28}
29
30pub struct AttributeParser {
31    inner: TokenIter,
32}
33
34pub struct Empty;
35
36impl From<Empty> for TokenStream {
37    fn from(_: Empty) -> TokenStream {
38        TokenStream::new()
39    }
40}
41
42impl AttributeParser {
43    pub fn new(stream: TokenStream) -> Self {
44        AttributeParser {
45            inner: stream.into_iter(),
46        }
47    }
48
49    pub fn parsed<T>(&mut self) -> Option<syn::Result<T>>
50    where
51        T: syn::parse::Parse,
52    {
53        let tokens = self.collect_tail(TokenStream::new());
54
55        if tokens.is_empty() {
56            return None;
57        }
58
59        Some(syn::parse2(tokens))
60    }
61
62    fn next_tt(&mut self) -> Option<TokenTree> {
63        expect_punct(self.inner.next(), ',')
64    }
65
66    fn collect_tail<T>(&mut self, first: T) -> TokenStream
67    where
68        T: Into<TokenStream>,
69    {
70        let mut out = first.into();
71
72        while let Some(tt) = self.next_tt() {
73            out.extend(Some(tt));
74        }
75
76        out
77    }
78
79    fn parse_unnamed(&mut self, first: Ident, next: TokenTree) -> Nested {
80        let mut out = TokenStream::from(TokenTree::Ident(first));
81
82        out.extend(self.collect_tail(next));
83
84        Nested::Unnamed(out.into_iter().collect())
85    }
86
87    fn parse_assign(&mut self, name: Ident) -> Nested {
88        let value = self.collect_tail(Empty);
89
90        Nested::Named(name, NestedValue::Assign(value))
91    }
92
93    fn parse_literal(&mut self, name: Ident, lit: Literal) -> Nested {
94        // TODO: Error if there are any tokens following
95        let _ = self.collect_tail(Empty);
96
97        Nested::Named(name, NestedValue::Literal(lit))
98    }
99
100    fn parse_group(&mut self, name: Ident, group: TokenStream) -> Nested {
101        Nested::Named(name, NestedValue::Group(group))
102    }
103
104    fn parse_keyword(&mut self, keyword: Ident, name: Ident) -> Nested {
105        let error = expect_punct(self.next_tt(), '=');
106
107        match error {
108            Some(error) => {
109                let error = self.collect_tail(error);
110
111                Nested::Unexpected(error)
112            }
113            None => {
114                let value = self.collect_tail(Empty);
115
116                Nested::Named(keyword, NestedValue::KeywordAssign(name, value))
117            }
118        }
119    }
120}
121
122impl Iterator for AttributeParser {
123    type Item = Nested;
124
125    fn next(&mut self) -> Option<Nested> {
126        let first = self.inner.next()?;
127
128        let name = match first {
129            TokenTree::Ident(ident) => ident,
130            tt => {
131                let stream = self.collect_tail(tt);
132
133                return Some(Nested::Unnamed(stream.into_iter().collect()));
134            }
135        };
136
137        match self.next_tt() {
138            Some(tt) if is_punct(&tt, '=') => Some(self.parse_assign(name)),
139            Some(TokenTree::Literal(lit)) => Some(self.parse_literal(name, lit)),
140            Some(TokenTree::Group(group)) => Some(self.parse_group(name, group.stream())),
141            Some(TokenTree::Ident(next)) => Some(self.parse_keyword(name, next)),
142            Some(next) => Some(self.parse_unnamed(name, next)),
143            None => Some(Nested::Unnamed(quote!(#name))),
144        }
145    }
146}