genco_derive/
lib.rs

1#![recursion_limit = "256"]
2
3extern crate proc_macro;
4
5use proc_macro2::{LineColumn, Span, Delimiter, Group, TokenTree, TokenStream};
6use syn::{LitStr, parse_macro_input, Result};
7use syn::parse::{ParseStream, Parse};
8use std::iter::FromIterator;
9use std::collections::VecDeque;
10
11/// Quotes the specified expression as a stream of tokens for use with genco.
12///
13/// # Mechanisms
14///
15/// * Elements are interpolated using `#`, so to include the variable `test`,
16///   you could write `#test`. Returned elements must implement
17///   [`FormatTokens`].
18/// * Inline statements can be evaluated using `#(<stmt>)`, or `#{<stmt>}`,
19///   or `#[<stmt>]`. In effect, anything that counts as a _group_ in Rust.
20///   For example: `#("test".quoted())` can be used to quote a string.
21/// * The [`register`] functionality of [`Tokens`] is available by prefixing an
22///   expression with `@` as `@<stmt>`.
23///   For example: `@only_imports`.
24/// * `#` and `@` can be escaped by repeating it twice in case it's needed in
25///   the target language. So `##` would produce a single `#`, and `@@` would
26///   produce a single `@`.
27///
28/// # Examples
29///
30/// ```rust
31/// #![feature(proc_macro_hygiene)]
32///
33/// use genco::rust::imported;
34/// use genco::{quote, Rust, Tokens};
35///
36/// // Import the LittleEndian item, without referencing it through the last
37/// // module component it is part of.
38/// let little_endian = imported("byteorder", "LittleEndian").qualified();
39/// let big_endian = imported("byteorder", "BigEndian");
40///
41/// // This is a trait, so only import it into the scope (unless we intent to
42/// // implement it).
43/// let write_bytes_ext = imported("byteorder", "WriteBytesExt").alias("_");
44///
45/// let tokens: Tokens<Rust> = quote! {
46///     @write_bytes_ext
47/// 
48///     let mut wtr = vec![];
49///     wtr.write_u16::<#little_endian>(517).unwrap();
50///     wtr.write_u16::<#big_endian>(768).unwrap();
51///     assert_eq!(wtr, vec![5, 2, 3, 0]);
52/// };
53///
54/// println!("{}", tokens.to_file_string().unwrap());
55/// ```
56///
57/// [`FormatTokens`]: https://docs.rs/genco/latest/genco/trait.FormatTokens.html
58/// [`register`]: https://docs.rs/genco/latest/genco/struct.Tokens.html#method.register
59#[proc_macro]
60pub fn quote(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
61    let Tokens(registers, output) = parse_macro_input!(input as Tokens);
62
63    let output = TokenStream::from_iter(output);
64
65    let gen = quote::quote! {{
66        let mut __toks = genco::Tokens::new();
67        #(#registers;)*
68        #output
69        __toks
70    }};
71
72    gen.into()
73}
74
75struct Tokens(Vec<TokenStream>, Vec<TokenTree>);
76
77#[derive(Clone, Copy, Debug)]
78struct Cursor {
79    start: LineColumn,
80    end: LineColumn,
81}
82
83impl Cursor {
84    /// Calculate the start character for the span.
85    fn start_character(self) -> Self {
86        Cursor {
87            start: self.start,
88            end: LineColumn {
89                line: self.start.line,
90                column: self.start.column + 1,
91            },
92        }
93    }
94
95    /// Calculate the end character for the span.
96    fn end_character(self) -> Self {
97        Cursor {
98            start: LineColumn {
99                line: self.end.line,
100                column: self.end.column - 1,
101            },
102            end: self.end,
103        }
104    }
105}
106
107impl From<Span> for Cursor {
108    fn from(span: Span) -> Self {
109        Self {
110            start: span.start(),
111            end: span.end(),
112        }
113    }
114}
115
116impl<'a> From<&'a Span> for Cursor {
117    fn from(span: &'a Span) -> Self {
118        Self {
119            start: span.start(),
120            end: span.end(),
121        }
122    }
123}
124
125impl Parse for Tokens {
126    fn parse(input: ParseStream) -> Result<Self> {
127        use std::iter::from_fn;
128
129        let mut registers = Vec::new();
130
131        let mut tokens = Vec::new();
132
133        let mut cursor = Cursor::from(input.span());
134        let mut last_column = cursor.start.column;
135
136        let mut queued = Vec::new();
137        let mut queue = VecDeque::new();
138
139        let mut item_buffer = ItemBuffer::new();
140
141        process_expressions(|item| queue.push_back(item), from_fn(move || {
142            if !input.is_empty() {
143                Some(input.parse::<TokenTree>())
144            } else {
145                None
146            }
147        }))?;
148
149        while let Some(item) = queue.pop_front() {
150            let next = item.cursor();
151
152            if cursor.start.line != next.start.line {
153                item_buffer.flush(&mut tokens);
154
155                debug_assert!(next.start.line > cursor.start.line);
156
157                let stream = if next.start.line - cursor.start.line > 1 {
158                    quote::quote!(__toks.line_spacing();)
159                } else {
160                    quote::quote!(__toks.push_spacing();)
161                };
162
163                tokens.push(TokenTree::Group(Group::new(Delimiter::None, stream)));
164
165                if last_column < next.start.column {
166                    let stream = quote::quote!(__toks.indent(););
167                    tokens.push(TokenTree::Group(Group::new(Delimiter::None, stream)));
168                } else if last_column > next.start.column {
169                    let stream = quote::quote!(__toks.unindent(););
170                    tokens.push(TokenTree::Group(Group::new(Delimiter::None, stream)));
171                }
172
173                last_column = next.start.column;
174            } else {
175                // Same line, but next item doesn't match.
176                if cursor.end.column < next.start.column && last_column != next.start.column {
177                    item_buffer.flush(&mut tokens);
178
179                    let stream = quote::quote!(__toks.spacing(););
180                    tokens.push(TokenTree::Group(Group::new(Delimiter::None, stream)));
181                }
182            }
183
184            cursor = next;
185
186            match item {
187                Item::Tree(tt) => {
188                    match tt {
189                        TokenTree::Group(group) => {
190                            process_expressions(|item| queued.push(item), group.stream().into_iter().map(Ok))?;
191
192                            match group.delimiter() {
193                                Delimiter::Parenthesis => item_buffer.push('('),
194                                Delimiter::Brace => item_buffer.push('{'),
195                                Delimiter::Bracket => item_buffer.push('['),
196                                _ => (),
197                            }
198
199                            let span_cursor = Cursor::from(group.span());
200                            queue.push_front(Item::DelimiterClose(span_cursor.end_character(), group.delimiter()));
201                            cursor = span_cursor.start_character();
202
203                            while let Some(item) = queued.pop() {
204                                queue.push_front(item);
205                            }
206                        }
207                        other => {
208                            item_buffer.push_str(&other.to_string());
209                        }
210                    }
211                }
212                Item::Expression(span, expr) => {
213                    item_buffer.flush(&mut tokens);
214
215                    let group = Group::new(Delimiter::None, quote::quote_spanned!(span => __toks.append(Clone::clone(&#expr));));
216                    tokens.push(TokenTree::Group(group));
217                }
218                Item::Register(span, expr) => {
219                    registers.push(quote::quote_spanned!(span => __toks.register(#expr)));
220                }
221                Item::DelimiterClose(_, delimiter) => {
222                    match delimiter {
223                        Delimiter::Parenthesis => item_buffer.push(')'),
224                        Delimiter::Brace => item_buffer.push('}'), 
225                        Delimiter::Bracket => item_buffer.push(']'),
226                        _ => (),
227                    }
228                }
229            }
230        }
231
232        item_buffer.flush(&mut tokens);
233        Ok(Self(registers, tokens))
234    }
235}
236
237struct ItemBuffer {
238    buffer: String,
239}
240
241impl ItemBuffer {
242    /// Construct a new line buffer.
243    fn new() -> Self {
244        Self {
245            buffer: String::new(),
246        }
247    }
248
249    /// Push the given character to the line buffer.
250    fn push(&mut self, c: char) {
251        self.buffer.push(c);
252    }
253
254    /// Push the given string to the line buffer.
255    fn push_str(&mut self, s: &str) {
256        self.buffer.push_str(s);
257    }
258
259    /// Flush the line buffer if necessary.
260    fn flush(&mut self, tokens: &mut Vec<TokenTree>) {
261        if !self.buffer.is_empty() {
262            let s = LitStr::new(&self.buffer, Span::call_site());
263            let group = Group::new(Delimiter::None, quote::quote!(__toks.append(#s);));
264            tokens.push(TokenTree::Group(group));
265            self.buffer.clear();
266        }
267    }
268}
269
270/// Items to process from the queue.
271#[derive(Debug)]
272enum Item {
273    Tree(TokenTree),
274    Expression(Span, TokenTree),
275    Register(Span, TokenTree),
276    DelimiterClose(Cursor, Delimiter),
277}
278
279impl Item {
280    fn cursor(&self) -> Cursor {
281        match self {
282            Self::Tree(tt) => Cursor::from(tt.span()),
283            Self::Expression(span, ..) => Cursor::from(span),
284            Self::Register(span, ..) => Cursor::from(span),
285            Self::DelimiterClose(cursor, ..) => *cursor,
286        }
287    }
288}
289
290/// Process expressions in the token stream.
291fn process_expressions(mut queue: impl FnMut(Item), mut it: impl Iterator<Item = Result<TokenTree>>) -> Result<()> {
292    let mut n1 = it.next().transpose()?;
293
294    while let Some(n0) = std::mem::replace(&mut n1, it.next().transpose()?) {
295        n1 = match (n0, n1) {
296            // Escape sequence for hash.
297            (TokenTree::Punct(mut a), Some(TokenTree::Punct(b))) if a.as_char() == '#' && b.as_char() == '#' => {
298                let span = a.span().join(b.span()).expect("failed to join spans");
299                a.set_span(span);
300                queue(Item::Tree(TokenTree::Punct(a)));
301                it.next().transpose()?
302            }
303            // Escape sequence for register.
304            (TokenTree::Punct(mut a), Some(TokenTree::Punct(b))) if a.as_char() == '@' && b.as_char() == '@' => {
305                let span = a.span().join(b.span()).expect("failed to join spans");
306                a.set_span(span);
307                queue(Item::Tree(TokenTree::Punct(a)));
308                it.next().transpose()?
309            }
310            // Context evaluation.
311            (TokenTree::Punct(first), Some(argument)) if first.as_char() == '#' => {
312                let span = first.span().join(argument.span()).expect("failed to join spans");
313
314                match argument {
315                    other => {
316                        queue(Item::Expression(span, other));
317                        it.next().transpose()?
318                    }
319                }
320            }
321            // Register evaluation.
322            (TokenTree::Punct(first), Some(argument)) if first.as_char() == '@' => {
323                let span = first.span().join(argument.span()).expect("failed to join spans");
324
325                match argument {
326                    other => {
327                        queue(Item::Register(span, other));
328                        it.next().transpose()?
329                    }
330                }
331            }
332            (tt, next) => {
333                queue(Item::Tree(tt));
334                next
335            }
336        }
337    }
338
339    if let Some(tt) = n1 {
340        queue(Item::Tree(tt));
341    }
342
343    Ok(())
344}