Skip to main content

zyn_core/ast/
interp_node.rs

1use proc_macro2::Ident;
2use proc_macro2::Span;
3use proc_macro2::TokenStream;
4use proc_macro2::TokenTree;
5
6use quote::ToTokens;
7use quote::quote;
8
9use syn::Token;
10use syn::parse::Parse;
11use syn::parse::ParseStream;
12
13use super::PipeNode;
14
15use crate::Expand;
16
17pub struct InterpNode {
18    pub span: Span,
19    pub expr: TokenStream,
20    pub pipes: Vec<PipeNode>,
21}
22
23impl InterpNode {
24    pub fn span(&self) -> Span {
25        self.span
26    }
27}
28
29impl Parse for InterpNode {
30    fn parse(input: ParseStream) -> syn::Result<Self> {
31        let content;
32        let brace = syn::braced!(content in input);
33        let span = brace.span.join();
34
35        let inner;
36        syn::braced!(inner in content);
37
38        let mut expr = TokenStream::new();
39        let mut pipes = Vec::new();
40
41        while !inner.is_empty() {
42            if inner.peek(Token![|]) {
43                inner.parse::<Token![|]>()?;
44                pipes.push(inner.parse::<PipeNode>()?);
45            } else if pipes.is_empty() {
46                let tt: TokenTree = inner.parse()?;
47                tt.to_tokens(&mut expr);
48            } else {
49                break;
50            }
51        }
52
53        if expr.is_empty() {
54            return Err(syn::Error::new(span, "empty interpolation"));
55        }
56
57        Ok(Self { span, expr, pipes })
58    }
59}
60
61impl Expand for InterpNode {
62    fn expand(&self, output: &Ident, idents: &mut crate::ident::Iter) -> TokenStream {
63        let expr = &self.expr;
64
65        if self.pipes.is_empty() {
66            return quote! {
67                ::zyn::quote::ToTokens::to_tokens(&(#expr), &mut #output);
68            };
69        }
70
71        let mut steps = vec![quote! { let __zyn_val = (#expr).to_string(); }];
72
73        for (i, pipe) in self.pipes.iter().enumerate() {
74            if i > 0 {
75                steps.push(quote! { let __zyn_val = __zyn_val.to_string(); });
76            }
77
78            steps.push(pipe.expand(output, idents));
79        }
80
81        quote! {
82            {
83                #(#steps)*
84                ::zyn::quote::ToTokens::to_tokens(&__zyn_val, &mut #output);
85            }
86        }
87    }
88}