Skip to main content

syn_rsx/
parser.rs

1//! RSX Parser
2
3use std::vec;
4
5use proc_macro2::{Punct, Span, TokenStream, TokenTree};
6use syn::{
7    braced,
8    ext::IdentExt,
9    parse::{discouraged::Speculative, Parse, ParseStream, Parser as _, Peek},
10    punctuated::Punctuated,
11    spanned::Spanned,
12    token::{Brace, Colon, Colon2},
13    Block, Error, Expr, ExprBlock, ExprLit, ExprPath, Ident, Path, PathSegment, Result, Token,
14};
15
16use crate::{config::TransformBlockFn, node::*, punctuation::*, ParserConfig};
17
18/// RSX Parser
19pub struct Parser {
20    config: ParserConfig,
21}
22
23impl Parser {
24    /// Create a new parser with the given [`ParserConfig`].
25    pub fn new(config: ParserConfig) -> Parser {
26        Parser { config }
27    }
28
29    /// Parse a given [`ParseStream`].
30    pub fn parse(&self, input: ParseStream) -> Result<Vec<Node>> {
31        let mut nodes = vec![];
32        let mut top_level_nodes = 0;
33        while !input.cursor().eof() {
34            let mut parsed_nodes = self.node(input)?;
35
36            if let Some(type_of_top_level_nodes) = &self.config.type_of_top_level_nodes {
37                if &parsed_nodes[0].r#type() != type_of_top_level_nodes {
38                    return Err(input.error(format!(
39                        "top level nodes need to be of type {}",
40                        type_of_top_level_nodes
41                    )));
42                }
43            }
44
45            top_level_nodes += 1;
46            nodes.append(&mut parsed_nodes);
47        }
48
49        if let Some(number_of_top_level_nodes) = &self.config.number_of_top_level_nodes {
50            if &top_level_nodes != number_of_top_level_nodes {
51                return Err(input.error(format!(
52                    "saw {} top level nodes but exactly {} are required",
53                    top_level_nodes, number_of_top_level_nodes
54                )));
55            }
56        }
57
58        Ok(nodes)
59    }
60
61    /// Parse the next [`Node`] in the tree.
62    ///
63    /// To improve performance it peeks the next 1-3 tokens and calls the
64    /// according node parser function depending on that.
65    fn node(&self, input: ParseStream) -> Result<Vec<Node>> {
66        let mut node = if input.peek(Token![<]) {
67            if input.peek2(Token![!]) {
68                if input.peek3(Ident) {
69                    self.doctype(input)
70                } else {
71                    self.comment(input)
72                }
73            } else if input.peek2(Token![>]) {
74                self.fragment(input)
75            } else {
76                self.element(input)
77            }
78        } else if input.peek(Brace) {
79            self.block(input)
80        } else {
81            self.text(input)
82        }?;
83
84        if self.config.flat_tree {
85            let mut children = node
86                .children_mut()
87                .map(|children| children.drain(..))
88                .into_iter()
89                .flatten()
90                .collect::<Vec<_>>();
91
92            let mut nodes = vec![node];
93            nodes.append(&mut children);
94            Ok(nodes)
95        } else {
96            Ok(vec![node])
97        }
98    }
99
100    /// Parse the stream as [`Node::Text`].
101    fn text(&self, input: ParseStream) -> Result<Node> {
102        let value = input.parse::<ExprLit>()?.into();
103
104        Ok(Node::Text(NodeText { value }))
105    }
106
107    /// Parse the stream as [`Node::Block`].
108    fn block(&self, input: ParseStream) -> Result<Node> {
109        let value = if let Some(transform_fn) = &self.config.transform_block {
110            self.block_transform(input, transform_fn)?
111        } else {
112            self.block_expr(input)?
113        }
114        .into();
115
116        Ok(Node::Block(NodeBlock { value }))
117    }
118
119    /// Replace the next [`TokenTree::Group`] in the given parse stream with a
120    /// token stream returned by a user callback, or parse as original block if
121    /// no token stream is returned.
122    fn block_transform(&self, input: ParseStream, transform_fn: &TransformBlockFn) -> Result<Expr> {
123        let parser = move |block_content: ParseStream| {
124            let forked_block_content = block_content.fork();
125
126            match transform_fn(&forked_block_content) {
127                Ok(transformed_tokens) => match transformed_tokens {
128                    Some(tokens) => {
129                        let parser = move |input: ParseStream| {
130                            Ok(self.block_content_to_block(input, block_content.span()))
131                        };
132                        let transformed_content = parser.parse2(tokens)?;
133                        block_content.advance_to(&forked_block_content);
134                        transformed_content
135                    }
136                    None => self.block_content_to_block(block_content, block_content.span()),
137                },
138                Err(error) => Err(error),
139            }
140        };
141
142        input.step(|cursor| {
143            let (tree, next) = cursor
144                .token_tree()
145                .ok_or_else(|| cursor.error("unexpected: no TokenTree found"))?;
146
147            match tree {
148                TokenTree::Group(block_group) => Ok((parser.parse2(block_group.stream())?, next)),
149                _ => Err(cursor.error("unexpected: no Group in TokenTree found")),
150            }
151        })
152    }
153
154    /// Parse the given stream and span as [`Expr::Block`].
155    fn block_content_to_block(&self, input: ParseStream, span: Span) -> Result<Expr> {
156        Ok(ExprBlock {
157            attrs: vec![],
158            label: None,
159            block: Block {
160                brace_token: Brace { span },
161                stmts: Block::parse_within(input)?,
162            },
163        }
164        .into())
165    }
166
167    /// Parse the given stream as [`Expr::Block`].
168    fn block_expr(&self, input: ParseStream) -> Result<Expr> {
169        let fork = input.fork();
170        let content;
171        let brace_token = braced!(content in fork);
172        let block = ExprBlock {
173            attrs: vec![],
174            label: None,
175            block: Block {
176                brace_token,
177                stmts: Block::parse_within(&content)?,
178            },
179        };
180        input.advance_to(&fork);
181
182        Ok(block.into())
183    }
184
185    /// Parse the given stream as [`NodeElement`].
186    fn element(&self, input: ParseStream) -> Result<Node> {
187        let fork = &input.fork();
188
189        if self.tag_close(&input.fork()).is_ok() {
190            return Err(fork.error("close tag has no corresponding open tag"));
191        }
192        let (name, attributes, self_closing) = self.tag_open(fork)?;
193
194        let mut children = vec![];
195        if !self_closing {
196            loop {
197                if !self.element_has_children(&name, fork)? {
198                    break;
199                }
200
201                children.append(&mut self.node(fork)?);
202            }
203
204            self.tag_close(fork)?;
205        }
206        input.advance_to(fork);
207
208        Ok(Node::Element(NodeElement {
209            name,
210            attributes,
211            children,
212        }))
213    }
214
215    /// Check whether the next token in the stream is a closing tag to decide
216    /// whether the node element has children.
217    fn element_has_children(&self, tag_open_name: &NodeName, input: ParseStream) -> Result<bool> {
218        // An empty input at this point means the tag wasn't closed.
219        if input.is_empty() {
220            return Err(Error::new(
221                tag_open_name.span(),
222                "open tag has no corresponding close tag and is not self-closing",
223            ));
224        }
225
226        if let Ok(tag_close_name) = self.tag_close(&input.fork()) {
227            if tag_open_name == &tag_close_name {
228                // If the next token is a matching close tag then there are no child nodes.
229                return Ok(false);
230            } else {
231                // If the next token is a closing tag with a different name it's an invalid
232                // tree.
233                return Err(input.error("close tag has no corresponding open tag"));
234            }
235        }
236
237        Ok(true)
238    }
239
240    /// Parse the stream as opening or self-closing tag and extract its
241    /// attributes.
242    fn tag_open(&self, input: ParseStream) -> Result<(NodeName, Vec<Node>, bool)> {
243        input.parse::<Token![<]>()?;
244        let name = self.node_name(input)?;
245
246        let mut attributes = TokenStream::new();
247        let self_closing = loop {
248            if let Ok(self_closing) = self.tag_open_end(input) {
249                break self_closing;
250            }
251
252            if input.is_empty() {
253                return Err(input.error("expected closing caret >"));
254            }
255
256            let next: TokenTree = input.parse()?;
257            attributes.extend(Some(next));
258        };
259
260        let attributes = if !attributes.is_empty() {
261            let parser = move |input: ParseStream| self.attributes(input);
262            parser.parse2(attributes)?
263        } else {
264            vec![]
265        };
266
267        Ok((name, attributes, self_closing))
268    }
269
270    /// Check whether an element tag ended or is self-closing.
271    fn tag_open_end(&self, input: ParseStream) -> Result<bool> {
272        let self_closing = input.parse::<Option<Token![/]>>()?.is_some();
273        input.parse::<Token![>]>()?;
274
275        Ok(self_closing)
276    }
277
278    /// Parse a closing tag and return its [`NodeName`].
279    fn tag_close(&self, input: ParseStream) -> Result<NodeName> {
280        input.parse::<Token![<]>()?;
281        input.parse::<Token![/]>()?;
282        let name = self.node_name(input)?;
283        input.parse::<Token![>]>()?;
284
285        Ok(name)
286    }
287
288    /// Parse the stream as vector of attributes.
289    fn attributes(&self, input: ParseStream) -> Result<Vec<Node>> {
290        let mut nodes = vec![];
291
292        loop {
293            if input.is_empty() {
294                break;
295            }
296
297            nodes.push(self.attribute(input)?);
298        }
299
300        Ok(nodes)
301    }
302
303    /// Parse the stream as [`Node::Attribute`].
304    fn attribute(&self, input: ParseStream) -> Result<Node> {
305        let fork = &input.fork();
306        if fork.peek(Brace) {
307            let value = self.block_expr(fork)?.into();
308            input.advance_to(fork);
309
310            Ok(Node::Block(NodeBlock { value }))
311        } else {
312            let key = self.node_name(fork)?;
313            let eq = fork.parse::<Option<Token![=]>>()?;
314            let value = if eq.is_some() {
315                if fork.is_empty() {
316                    return Err(Error::new(key.span(), "missing attribute value"));
317                }
318
319                if fork.peek(Brace) {
320                    Some(NodeValueExpr::new(self.block_expr(fork)?))
321                } else {
322                    Some(NodeValueExpr::new(fork.parse()?))
323                }
324            } else {
325                None
326            };
327            input.advance_to(fork);
328
329            Ok(Node::Attribute(NodeAttribute { key, value }))
330        }
331    }
332
333    /// Parse the stream as [`Node::Doctype`].
334    fn doctype(&self, input: ParseStream) -> Result<Node> {
335        input.parse::<Token![<]>()?;
336        input.parse::<Token![!]>()?;
337        let ident = input.parse::<Ident>()?;
338        if ident.to_string().to_lowercase() != "doctype" {
339            return Err(input.error("expected Doctype"));
340        }
341        let doctype = input.parse::<Ident>()?;
342        input.parse::<Token![>]>()?;
343
344        let mut segments = Punctuated::new();
345        segments.push_value(PathSegment::from(doctype));
346        let value = NodeValueExpr::new(
347            ExprPath {
348                attrs: vec![],
349                qself: None,
350                path: Path {
351                    leading_colon: None,
352                    segments,
353                },
354            }
355            .into(),
356        );
357
358        Ok(Node::Doctype(NodeDoctype { value }))
359    }
360
361    /// Parse the stream as [`Node::Comment`].
362    fn comment(&self, input: ParseStream) -> Result<Node> {
363        input.parse::<Token![<]>()?;
364        input.parse::<Token![!]>()?;
365        input.parse::<Token![-]>()?;
366        input.parse::<Token![-]>()?;
367        let value = NodeValueExpr::new(input.parse::<ExprLit>()?.into());
368        input.parse::<Token![-]>()?;
369        input.parse::<Token![-]>()?;
370        input.parse::<Token![>]>()?;
371
372        Ok(Node::Comment(NodeComment { value }))
373    }
374
375    /// Parse the stream as [`Node::Fragement`].
376    fn fragment(&self, input: ParseStream) -> Result<Node> {
377        self.fragment_open(input)?;
378
379        let mut children = vec![];
380        loop {
381            if input.is_empty() {
382                return Err(input.error("unexpected end of input"));
383            }
384
385            if self.fragment_close(&input.fork()).is_ok() {
386                self.fragment_close(input)?;
387                break;
388            }
389
390            children.append(&mut self.node(input)?);
391        }
392
393        Ok(Node::Fragment(NodeFragment { children }))
394    }
395
396    /// Parse the stream as opening fragment tag.
397    fn fragment_open(&self, input: ParseStream) -> Result<()> {
398        input.parse::<Token![<]>()?;
399        input.parse::<Token![>]>()?;
400
401        Ok(())
402    }
403
404    /// Parse the stream as closing fragment tag.
405    fn fragment_close(&self, input: ParseStream) -> Result<()> {
406        input.parse::<Token![<]>()?;
407        input.parse::<Token![/]>()?;
408        input.parse::<Token![>]>()?;
409
410        Ok(())
411    }
412
413    /// Parse the stream as [`NodeName`].
414    fn node_name(&self, input: ParseStream) -> Result<NodeName> {
415        if input.peek2(Colon2) {
416            self.node_name_punctuated_ident::<Colon2, fn(_) -> Colon2, PathSegment>(input, Colon2)
417                .map(|segments| {
418                    NodeName::Path(ExprPath {
419                        attrs: vec![],
420                        qself: None,
421                        path: Path {
422                            leading_colon: None,
423                            segments,
424                        },
425                    })
426                })
427        } else if input.peek2(Colon) || input.peek2(Dash) {
428            self.node_name_punctuated_ident_with_alternate::<Punct, fn(_) -> Colon, fn(_) -> Dash, Ident>(
429                input, Colon, Dash,
430            )
431            .map(NodeName::Punctuated)
432        } else if input.peek(Brace) {
433            let fork = &input.fork();
434            let value = self.block_expr(fork)?;
435            input.advance_to(fork);
436            Ok(NodeName::Block(value))
437        } else if input.peek(Ident::peek_any) {
438            let mut segments = Punctuated::new();
439            let ident = Ident::parse_any(input)?;
440            segments.push_value(PathSegment::from(ident));
441            Ok(NodeName::Path(ExprPath {
442                attrs: vec![],
443                qself: None,
444                path: Path {
445                    leading_colon: None,
446                    segments,
447                },
448            }))
449        } else {
450            Err(input.error("invalid tag name or attribute key"))
451        }
452    }
453
454    /// Parse the stream as punctuated idents.
455    ///
456    /// We can't replace this with [`Punctuated::parse_separated_nonempty`]
457    /// since that doesn't support reserved keywords. Might be worth to
458    /// consider a PR upstream.
459    ///
460    /// [`Punctuated::parse_separated_nonempty`]: https://docs.rs/syn/1.0.58/syn/punctuated/struct.Punctuated.html#method.parse_separated_nonempty
461    fn node_name_punctuated_ident<T: Parse, F: Peek, X: From<Ident>>(
462        &self,
463        input: ParseStream,
464        punct: F,
465    ) -> Result<Punctuated<X, T>> {
466        let fork = &input.fork();
467        let mut segments = Punctuated::<X, T>::new();
468
469        while !fork.is_empty() && fork.peek(Ident::peek_any) {
470            let ident = Ident::parse_any(fork)?;
471            segments.push_value(ident.clone().into());
472
473            if fork.peek(punct) {
474                segments.push_punct(fork.parse()?);
475            } else {
476                break;
477            }
478        }
479
480        if segments.len() > 1 {
481            input.advance_to(fork);
482            Ok(segments)
483        } else {
484            Err(fork.error("expected punctuated node name"))
485        }
486    }
487
488    /// Parse the stream as punctuated idents, with two possible punctuations
489    /// available
490    fn node_name_punctuated_ident_with_alternate<T: Parse, F: Peek, G: Peek, X: From<Ident>>(
491        &self,
492        input: ParseStream,
493        punct: F,
494        alternate_punct: G,
495    ) -> Result<Punctuated<X, T>> {
496        let fork = &input.fork();
497        let mut segments = Punctuated::<X, T>::new();
498
499        while !fork.is_empty() && fork.peek(Ident::peek_any) {
500            let ident = Ident::parse_any(fork)?;
501            segments.push_value(ident.clone().into());
502
503            if fork.peek(punct) || fork.peek(alternate_punct) {
504                segments.push_punct(fork.parse()?);
505            } else {
506                break;
507            }
508        }
509
510        if segments.len() > 1 {
511            input.advance_to(fork);
512            Ok(segments)
513        } else {
514            Err(fork.error("expected punctuated node name"))
515        }
516    }
517}