Skip to main content

moxy_token/
stream.rs

1use std::str::FromStr;
2
3use super::ToTokens;
4use crate::lex::{Cursor, LexError, Scan};
5use crate::span::DelimSpan;
6use crate::{Span, Spanner, Token, TokenTree};
7
8#[derive(Debug, Default, Clone, PartialEq, Eq)]
9pub struct TokenStream(Vec<TokenTree>);
10
11impl TokenStream {
12    #[inline]
13    pub fn new() -> Self {
14        Self(vec![])
15    }
16
17    #[inline]
18    pub fn with_capacity(capacity: usize) -> Self {
19        Self(Vec::with_capacity(capacity))
20    }
21
22    #[inline]
23    pub fn is_empty(&self) -> bool {
24        self.0.is_empty()
25    }
26
27    #[inline]
28    pub fn iter(&self) -> impl Iterator<Item = &TokenTree> {
29        self.0.iter()
30    }
31
32    #[inline]
33    pub fn first(&self) -> Span {
34        self.0.first().map(|v| v.span()).unwrap_or_default()
35    }
36
37    #[inline]
38    pub fn last(&self) -> Span {
39        self.0.last().map(|v| v.span()).unwrap_or_default()
40    }
41
42    #[inline]
43    pub fn span(&self) -> Span {
44        self.first().join(self.last())
45    }
46
47    #[inline]
48    pub fn delim(&self) -> DelimSpan {
49        DelimSpan::new(self.first(), self.last())
50    }
51
52    #[inline]
53    pub fn extend_one(&mut self, token: TokenTree) {
54        self.0.push(token);
55    }
56
57    #[inline]
58    pub fn into_inner(self) -> Vec<TokenTree> {
59        self.0
60    }
61
62    #[inline]
63    pub fn to_vec(self) -> Vec<TokenTree> {
64        self.0
65    }
66}
67
68impl std::ops::Deref for TokenStream {
69    type Target = [TokenTree];
70
71    fn deref(&self) -> &[TokenTree] {
72        self.0.as_slice()
73    }
74}
75
76impl Extend<TokenTree> for TokenStream {
77    fn extend<T: IntoIterator<Item = TokenTree>>(&mut self, iter: T) {
78        self.0.extend(iter);
79    }
80}
81
82impl FromIterator<TokenTree> for TokenStream {
83    fn from_iter<T: IntoIterator<Item = TokenTree>>(iter: T) -> Self {
84        Self(iter.into_iter().collect())
85    }
86}
87
88impl FromIterator<Self> for TokenStream {
89    fn from_iter<T: IntoIterator<Item = Self>>(iter: T) -> Self {
90        Self(iter.into_iter().flat_map(|s| s.into_iter()).collect())
91    }
92}
93
94impl IntoIterator for TokenStream {
95    type Item = TokenTree;
96    type IntoIter = std::vec::IntoIter<TokenTree>;
97
98    fn into_iter(self) -> Self::IntoIter {
99        self.0.into_iter()
100    }
101}
102
103impl From<Vec<TokenTree>> for TokenStream {
104    #[inline]
105    fn from(value: Vec<TokenTree>) -> Self {
106        Self(value)
107    }
108}
109
110impl From<&[TokenTree]> for TokenStream {
111    #[inline]
112    fn from(value: &[TokenTree]) -> Self {
113        Self(value.to_vec())
114    }
115}
116
117impl From<TokenStream> for Vec<TokenTree> {
118    #[inline]
119    fn from(value: TokenStream) -> Self {
120        value.0
121    }
122}
123
124impl Spanner for TokenStream {
125    fn span(&self) -> Span {
126        self.first().join(self.last())
127    }
128}
129
130impl Scan for TokenStream {
131    fn scan(cursor: Cursor<'_>) -> Result<(Cursor<'_>, Self), LexError> {
132        let mut tokens = Vec::with_capacity(8);
133        let mut c = cursor;
134
135        loop {
136            c = c.skip_whitespace();
137
138            if c.is_empty() {
139                break;
140            }
141
142            // Doc comment → `#[doc = "..."]` (outer) / `#![doc = "..."]` (inner).
143            if let Some((next, inner, text)) = c.doc_comment() {
144                push_doc_attr(&mut tokens, inner, &text, c.span_to(&next));
145                c = next;
146                continue;
147            }
148
149            // Check for closing delimiter — return to caller (Group::scan handles matching)
150            if let Some(')' | ']' | '}') = c.first() {
151                break;
152            }
153
154            // Try group first (opening delimiter)
155            if let Ok((next, group)) = crate::Group::scan(c) {
156                tokens.push(TokenTree::Group(group));
157                c = next;
158                continue;
159            }
160
161            if let Ok((next, lit)) = crate::Lit::scan(c) {
162                tokens.push(TokenTree::Literal(lit));
163                c = next;
164                continue;
165            }
166
167            if let Ok((next, keyword)) = crate::Keyword::scan(c) {
168                tokens.push(TokenTree::Keyword(keyword));
169                c = next;
170                continue;
171            }
172
173            if let Ok((next, ident)) = crate::Ident::scan(c) {
174                tokens.push(TokenTree::Ident(ident));
175                c = next;
176                continue;
177            }
178
179            if let Ok((next, punct)) = crate::Punct::scan(c) {
180                tokens.push(TokenTree::Punct(punct));
181                c = next;
182                continue;
183            }
184
185            return Err(c
186                .error()
187                .message(format!("unexpected character '{}'", c.first().unwrap_or('\0'))));
188        }
189
190        Ok((c, Self(tokens)))
191    }
192}
193
194impl FromStr for TokenStream {
195    type Err = LexError;
196
197    fn from_str(s: &str) -> Result<Self, Self::Err> {
198        Self::from_string(s.to_owned())
199    }
200}
201
202impl TokenStream {
203    /// Build a token stream from an owned string while moving that allocation
204    /// directly into the fallback source map.
205    pub fn from_string(s: String) -> Result<Self, LexError> {
206        use crate::source::SourceMap;
207
208        SourceMap::with_mut(|sm| {
209            let span = sm.push(s);
210            let source = sm.find(span).expect("new source missing from source map");
211            let cursor = Cursor::new(source.text(), span.byte_range().start as u32);
212            let (rest, stream) = Self::scan(cursor)?;
213            let rest = rest.skip_whitespace();
214
215            if !rest.is_empty() {
216                return rest.error().message("unexpected trailing input").into();
217            }
218
219            Ok(stream)
220        })
221    }
222}
223
224impl std::fmt::Display for TokenStream {
225    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
226        let mut first = true;
227        let mut prev_was_joint = false;
228
229        for tt in self.0.iter() {
230            if !first && !prev_was_joint {
231                write!(f, " ")?;
232            }
233
234            write!(f, "{}", tt)?;
235            first = false;
236            prev_was_joint = matches!(tt, TokenTree::Punct(punct) if punct.spacing().is_joint());
237        }
238
239        Ok(())
240    }
241}
242
243impl ToTokens for TokenStream {
244    fn to_tokens(&self, tokens: &mut Self) {
245        tokens.extend(self.clone());
246    }
247}
248
249#[cfg(feature = "serde")]
250impl serde::Serialize for TokenStream {
251    fn serialize<S>(&self, s: S) -> Result<S::Ok, S::Error>
252    where
253        S: serde::Serializer,
254    {
255        self.0.serialize(s)
256    }
257}
258
259fn push_doc_attr(tokens: &mut Vec<TokenTree>, inner: bool, text: &str, span: Span) {
260    use crate::{Delim, Group, Ident, Punct};
261
262    tokens.push(TokenTree::Punct(Punct::Pound(<Token![#]>::new(span))));
263
264    if inner {
265        tokens.push(TokenTree::Punct(Punct::Not(<Token![!]>::new(span))));
266    }
267
268    let mut body = TokenStream::with_capacity(3);
269    body.extend_one(TokenTree::Ident(Ident::new("doc").with_span(span)));
270    body.extend_one(TokenTree::Punct(Punct::Eq(<Token![=]>::new(span))));
271    body.extend_one(TokenTree::Literal(crate::Lit::Str(crate::LitStr::new(text, span))));
272
273    tokens.push(TokenTree::Group(Group::new(Delim::Bracket, body)));
274}