yul_parser/
parser.rs

1use crate::{Lexer, Node, Parse, SyntaxKind, Token, YulLanguage};
2use core::iter::Peekable;
3use rowan::{Checkpoint, GreenNodeBuilder, Language};
4
5/// Wrapper around Logos and Rowan to for parsing with 1 lookahead.
6pub struct Parser<'a> {
7    lexer:   Peekable<Lexer<'a>>,
8    builder: GreenNodeBuilder<'static>,
9}
10
11impl<'a> Parser<'a> {
12    pub fn new(input: &'a str) -> Self {
13        let mut parser = Self {
14            lexer:   Lexer::new(input).peekable(),
15            builder: GreenNodeBuilder::new(),
16        };
17        parser.start_node(Node::root());
18        parser
19    }
20
21    /// Finishes the parse, any remaining content will be consumed as an error.
22    pub fn finish(mut self) -> Parse {
23        if self.peek().is_some() {
24            self.start_node(Node::Error);
25            while self.peek().is_some() {
26                self.bump();
27            }
28            self.finish_node();
29        }
30        self.finish_node();
31        Parse::new(self.builder.finish())
32    }
33
34    /// True if the next token is `token`.
35    pub fn peek_is(&mut self, token: Token) -> bool {
36        self.peek().map_or(false, |peek| peek == token)
37    }
38
39    /// True if the next token is not `token` or end-of-file.
40    pub fn peek_is_not(&mut self, token: Token) -> bool {
41        self.peek().map_or(false, |peek| peek != token)
42    }
43
44    /// Peek at the first unprocessed token
45    pub fn peek(&mut self) -> Option<Token> {
46        self.lexer.peek().map(|(token, _)| *token)
47    }
48
49    /// Parse a specific token and skip trailing insignificants
50    pub fn token(&mut self, token: Token) {
51        if self.peek() == Some(token) {
52            self.bump();
53        } else {
54            self.start_node(Node::Error);
55            if self.peek().is_some() {
56                self.bump();
57            }
58            self.finish_node();
59            // todo!();
60        }
61        self.skip_insignificant();
62    }
63
64    /// Add the next significant token in an error node.
65    pub fn error(&mut self) {
66        self.start_node(Node::error());
67        self.bump();
68        self.skip_insignificant();
69        self.finish_node();
70    }
71
72    /// Skip insignificant tokens
73    pub fn skip_insignificant(&mut self) {
74        while !self.peek().as_ref().map_or(true, Token::is_significant) {
75            self.bump()
76        }
77    }
78
79    /// Advance one token, adding it to the current branch of the tree builder.
80    ///
81    /// # Panics
82    /// Panics when called on end-of-file.
83    pub fn bump(&mut self) {
84        let (token, text) = self.lexer.next().unwrap();
85        self.add_token(token, text);
86    }
87
88    /// Prepare for maybe wrapping the next node.
89    pub fn checkpoint(&self) -> Checkpoint {
90        self.builder.checkpoint()
91    }
92
93    /// Wrap the previous branch marked by `checkpoint` in a new branch and make
94    /// it current.
95    pub fn start_node_at(&mut self, checkpoint: Checkpoint, node: Node) {
96        let kind = SyntaxKind::Node(node);
97        self.builder
98            .start_node_at(checkpoint, YulLanguage::kind_to_raw(kind));
99    }
100
101    /// Add a new parent node to the tree and decent.
102    pub fn start_node(&mut self, node: Node) {
103        let kind = SyntaxKind::Node(node);
104        self.builder.start_node(YulLanguage::kind_to_raw(kind));
105    }
106
107    /// Finish the current branch and ascent to the parent node.
108    pub fn finish_node(&mut self) {
109        self.builder.finish_node();
110    }
111
112    /// Add a token to the current branch of the tree builder.
113    pub fn add_token(&mut self, token: Token, slice: &str) {
114        let kind = SyntaxKind::Token(token);
115        self.builder
116            .token(YulLanguage::kind_to_raw(kind), slice.into());
117    }
118}
119
120#[cfg(test)]
121mod tests {
122    use super::*;
123    use serde_json::json;
124
125    #[test]
126    fn parse_nothing() {
127        let parser = Parser::new("");
128        let parse = parser.finish();
129        assert_eq!(parse.debug_tree(), "Root@0..0");
130    }
131
132    #[test]
133    fn parse_number() {
134        let mut parser = Parser::new("123");
135        parser.token(Token::LiteralInt);
136        let parse = parser.finish();
137        assert_eq!(
138            serde_json::to_value(&parse).unwrap(),
139            json!({
140                "kind": "Root",
141                "text_range": [0, 3],
142                "children": [{
143                    "kind": "LiteralInt",
144                    "text_range": [0, 3],
145                    "text": "123"
146                }]
147            })
148        );
149    }
150}