brack_parser/
parse.rs

1use anyhow::Result;
2use brack_sdk_rs::ast::AST;
3use brack_tokenizer::tokens::Token;
4
5use crate::{
6    ast::new_document,
7    stmt,
8};
9
10pub fn parse(tokens: &Vec<Token>) -> Result<AST> {
11    let mut new_tokens = tokens.clone();
12    let mut result = new_document();
13
14    while new_tokens.len() > 0 {
15        match stmt::parse(&new_tokens) {
16            Ok((ast, tokens)) => {
17                new_tokens = tokens;
18                result.add(ast)?;
19            }
20            Err(e) => return Err(e),
21        }
22    }
23
24    Ok(result)
25}
26
27#[cfg(test)]
28mod test {
29    use anyhow::Result;
30    use brack_tokenizer::tokens::{mock_location, Token};
31
32    use crate::ast::{
33        assert_ast_eq, new_angle_with_children, new_curly_with_children,
34        new_document_with_children, new_expr_with_children, new_ident, new_square_with_children,
35        new_stmt_with_children, new_text,
36    };
37
38    use super::parse;
39
40    #[test]
41    fn test_parse_no_commands() -> Result<()> {
42        let tokens = vec![
43            Token::Text("Hello, World!".to_string(), mock_location()),
44            Token::EOF(mock_location()),
45        ];
46        let parsed = parse(&tokens)?;
47        let expected =
48            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
49                vec![new_text("Hello, World!".to_string())],
50            )])]);
51        assert_ast_eq(&parsed, &expected);
52        Ok(())
53    }
54
55    #[test]
56    fn test_parse_commands_with_an_argument_includes_square_brackets() -> Result<()> {
57        let tokens = vec![
58            Token::Text("Hello, ".to_string(), mock_location()),
59            Token::SquareBracketOpen(mock_location()),
60            Token::Module("std".to_string(), mock_location()),
61            Token::Dot(mock_location()),
62            Token::Ident("*".to_string(), mock_location()),
63            Token::Text("World!".to_string(), mock_location()),
64            Token::SquareBracketClose(mock_location()),
65            Token::EOF(mock_location()),
66        ];
67        let parsed = parse(&tokens)?;
68        let expected =
69            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
70                vec![
71                    new_text("Hello, ".to_string()),
72                    new_square_with_children(vec![
73                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
74                        new_expr_with_children(vec![new_text("World!".to_string())]),
75                    ]),
76                ],
77            )])]);
78        assert_ast_eq(&parsed, &expected);
79        Ok(())
80    }
81
82    #[test]
83    fn test_parse_commands_with_an_argument_includes_curly_brackets() -> Result<()> {
84        let tokens = vec![
85            Token::CurlyBracketOpen(mock_location()),
86            Token::Module("std".to_string(), mock_location()),
87            Token::Dot(mock_location()),
88            Token::Ident("*".to_string(), mock_location()),
89            Token::Text("Heading".to_string(), mock_location()),
90            Token::CurlyBracketClose(mock_location()),
91            Token::NewLine(mock_location()),
92            Token::Text("Hello, World!".to_string(), mock_location()),
93            Token::EOF(mock_location()),
94        ];
95        let parsed = parse(&tokens)?;
96        let expected = new_document_with_children(vec![
97            new_stmt_with_children(vec![new_curly_with_children(vec![
98                new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
99                new_expr_with_children(vec![new_text("Heading".to_string())]),
100            ])]),
101            new_stmt_with_children(vec![new_expr_with_children(vec![new_text(
102                "Hello, World!".to_string(),
103            )])]),
104        ]);
105        assert_ast_eq(&parsed, &expected);
106        Ok(())
107    }
108
109    #[test]
110    fn test_parse_commands_with_an_argument_includes_angle_brackets() -> Result<()> {
111        let tokens = vec![
112            Token::Text("Hello, ".to_string(), mock_location()),
113            Token::AngleBracketOpen(mock_location()),
114            Token::Module("std".to_string(), mock_location()),
115            Token::Dot(mock_location()),
116            Token::Ident("*".to_string(), mock_location()),
117            Token::Text("World!".to_string(), mock_location()),
118            Token::AngleBracketClose(mock_location()),
119            Token::EOF(mock_location()),
120        ];
121        let parsed = parse(&tokens)?;
122        let expected =
123            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
124                vec![
125                    new_text("Hello, ".to_string()),
126                    new_angle_with_children(vec![
127                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
128                        new_expr_with_children(vec![new_text("World!".to_string())]),
129                    ]),
130                ],
131            )])]);
132        assert_ast_eq(&parsed, &expected);
133        Ok(())
134    }
135
136    #[test]
137    fn test_parse_commands_with_two_arguments_includes_square_brackets() -> Result<()> {
138        let tokens = vec![
139            Token::Text("Hello, ".to_string(), mock_location()),
140            Token::SquareBracketOpen(mock_location()),
141            Token::Module("std".to_string(), mock_location()),
142            Token::Dot(mock_location()),
143            Token::Ident("@".to_string(), mock_location()),
144            Token::Text("World!".to_string(), mock_location()),
145            Token::Comma(mock_location()),
146            Token::Text("https://example.com/".to_string(), mock_location()),
147            Token::SquareBracketClose(mock_location()),
148            Token::EOF(mock_location()),
149        ];
150        let parsed = parse(&tokens)?;
151        let expected =
152            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
153                vec![
154                    new_text("Hello, ".to_string()),
155                    new_square_with_children(vec![
156                        new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
157                        new_expr_with_children(vec![new_text("World!".to_string())]),
158                        new_expr_with_children(vec![new_text("https://example.com/".to_string())]),
159                    ]),
160                ],
161            )])]);
162        assert_ast_eq(&parsed, &expected);
163        Ok(())
164    }
165
166    #[test]
167    fn test_parse_nesting_commands() -> Result<()> {
168        let tokens = vec![
169            Token::Text("Hello, ".to_string(), mock_location()),
170            Token::SquareBracketOpen(mock_location()),
171            Token::Module("std".to_string(), mock_location()),
172            Token::Dot(mock_location()),
173            Token::Ident("*".to_string(), mock_location()),
174            Token::SquareBracketOpen(mock_location()),
175            Token::Module("std".to_string(), mock_location()),
176            Token::Dot(mock_location()),
177            Token::Ident("@".to_string(), mock_location()),
178            Token::Text("World!".to_string(), mock_location()),
179            Token::Comma(mock_location()),
180            Token::Text("https://example.com/".to_string(), mock_location()),
181            Token::SquareBracketClose(mock_location()),
182            Token::SquareBracketClose(mock_location()),
183            Token::EOF(mock_location()),
184        ];
185        let parsed = parse(&tokens)?;
186        let expected =
187            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
188                vec![
189                    new_text("Hello, ".to_string()),
190                    new_square_with_children(vec![
191                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
192                        new_expr_with_children(vec![new_square_with_children(vec![
193                            new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
194                            new_expr_with_children(vec![new_text("World!".to_string())]),
195                            new_expr_with_children(vec![new_text(
196                                "https://example.com/".to_string(),
197                            )]),
198                        ])]),
199                    ]),
200                ],
201            )])]);
202        assert_ast_eq(&parsed, &expected);
203        Ok(())
204    }
205
206    #[test]
207    fn test_parse_newlines() -> Result<()> {
208        let tokens = vec![
209            Token::Text("Hello,".to_string(), mock_location()),
210            Token::NewLine(mock_location()),
211            Token::Text("World,".to_string(), mock_location()),
212            Token::NewLine(mock_location()),
213            Token::CurlyBracketOpen(mock_location()),
214            Token::Module("std".to_string(), mock_location()),
215            Token::Dot(mock_location()),
216            Token::Ident("**".to_string(), mock_location()),
217            Token::Text("Contact".to_string(), mock_location()),
218            Token::CurlyBracketClose(mock_location()),
219            Token::NewLine(mock_location()),
220            Token::SquareBracketOpen(mock_location()),
221            Token::Module("std".to_string(), mock_location()),
222            Token::Dot(mock_location()),
223            Token::Ident("@".to_string(), mock_location()),
224            Token::Text("My website".to_string(), mock_location()),
225            Token::Comma(mock_location()),
226            Token::Text("https://example.com/".to_string(), mock_location()),
227            Token::SquareBracketClose(mock_location()),
228            Token::NewLine(mock_location()),
229            Token::NewLine(mock_location()),
230            Token::Text("2023.12.28".to_string(), mock_location()),
231            Token::NewLine(mock_location()),
232            Token::EOF(mock_location()),
233        ];
234        let parsed = parse(&tokens)?;
235        let expected = new_document_with_children(vec![
236            new_stmt_with_children(vec![
237                new_expr_with_children(vec![new_text("Hello,".to_string())]),
238                new_expr_with_children(vec![new_text("World,".to_string())]),
239            ]),
240            new_stmt_with_children(vec![new_curly_with_children(vec![
241                new_ident(vec![
242                    new_text("std".to_string()),
243                    new_text("**".to_string()),
244                ]),
245                new_expr_with_children(vec![new_text("Contact".to_string())]),
246            ])]),
247            new_stmt_with_children(vec![new_expr_with_children(vec![
248                new_square_with_children(vec![
249                    new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
250                    new_expr_with_children(vec![new_text("My website".to_string())]),
251                    new_expr_with_children(vec![new_text("https://example.com/".to_string())]),
252                ]),
253            ])]),
254            new_stmt_with_children(vec![new_expr_with_children(vec![new_text(
255                "2023.12.28".to_string(),
256            )])]),
257        ]);
258        assert_ast_eq(&parsed, &expected);
259        Ok(())
260    }
261}