brack_parser/
parse.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
use anyhow::Result;
use brack_sdk_rs::ast::AST;
use brack_tokenizer::tokens::Token;

use crate::{
    ast::new_document,
    stmt,
};

pub fn parse(tokens: &Vec<Token>) -> Result<AST> {
    let mut new_tokens = tokens.clone();
    let mut result = new_document();

    while new_tokens.len() > 0 {
        match stmt::parse(&new_tokens) {
            Ok((ast, tokens)) => {
                new_tokens = tokens;
                result.add(ast)?;
            }
            Err(e) => return Err(e),
        }
    }

    Ok(result)
}

#[cfg(test)]
mod test {
    use anyhow::Result;
    use brack_tokenizer::tokens::{mock_location, Token};

    use crate::ast::{
        assert_ast_eq, new_angle_with_children, new_curly_with_children,
        new_document_with_children, new_expr_with_children, new_ident, new_square_with_children,
        new_stmt_with_children, new_text,
    };

    use super::parse;

    #[test]
    fn test_parse_no_commands() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, World!".to_string(), mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![new_text("Hello, World!".to_string())],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_an_argument_includes_square_brackets() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_square_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                        new_expr_with_children(vec![new_text("World!".to_string())]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_an_argument_includes_curly_brackets() -> Result<()> {
        let tokens = vec![
            Token::CurlyBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::Text("Heading".to_string(), mock_location()),
            Token::CurlyBracketClose(mock_location()),
            Token::NewLine(mock_location()),
            Token::Text("Hello, World!".to_string(), mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected = new_document_with_children(vec![
            new_stmt_with_children(vec![new_curly_with_children(vec![
                new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                new_expr_with_children(vec![new_text("Heading".to_string())]),
            ])]),
            new_stmt_with_children(vec![new_expr_with_children(vec![new_text(
                "Hello, World!".to_string(),
            )])]),
        ]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_an_argument_includes_angle_brackets() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::AngleBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::AngleBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_angle_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                        new_expr_with_children(vec![new_text("World!".to_string())]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_commands_with_two_arguments_includes_square_brackets() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("@".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::Comma(mock_location()),
            Token::Text("https://example.com/".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_square_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
                        new_expr_with_children(vec![new_text("World!".to_string())]),
                        new_expr_with_children(vec![new_text("https://example.com/".to_string())]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_nesting_commands() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello, ".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("*".to_string(), mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("@".to_string(), mock_location()),
            Token::Text("World!".to_string(), mock_location()),
            Token::Comma(mock_location()),
            Token::Text("https://example.com/".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected =
            new_document_with_children(vec![new_stmt_with_children(vec![new_expr_with_children(
                vec![
                    new_text("Hello, ".to_string()),
                    new_square_with_children(vec![
                        new_ident(vec![new_text("std".to_string()), new_text("*".to_string())]),
                        new_expr_with_children(vec![new_square_with_children(vec![
                            new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
                            new_expr_with_children(vec![new_text("World!".to_string())]),
                            new_expr_with_children(vec![new_text(
                                "https://example.com/".to_string(),
                            )]),
                        ])]),
                    ]),
                ],
            )])]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }

    #[test]
    fn test_parse_newlines() -> Result<()> {
        let tokens = vec![
            Token::Text("Hello,".to_string(), mock_location()),
            Token::NewLine(mock_location()),
            Token::Text("World,".to_string(), mock_location()),
            Token::NewLine(mock_location()),
            Token::CurlyBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("**".to_string(), mock_location()),
            Token::Text("Contact".to_string(), mock_location()),
            Token::CurlyBracketClose(mock_location()),
            Token::NewLine(mock_location()),
            Token::SquareBracketOpen(mock_location()),
            Token::Module("std".to_string(), mock_location()),
            Token::Dot(mock_location()),
            Token::Ident("@".to_string(), mock_location()),
            Token::Text("My website".to_string(), mock_location()),
            Token::Comma(mock_location()),
            Token::Text("https://example.com/".to_string(), mock_location()),
            Token::SquareBracketClose(mock_location()),
            Token::NewLine(mock_location()),
            Token::NewLine(mock_location()),
            Token::Text("2023.12.28".to_string(), mock_location()),
            Token::NewLine(mock_location()),
            Token::EOF(mock_location()),
        ];
        let parsed = parse(&tokens)?;
        let expected = new_document_with_children(vec![
            new_stmt_with_children(vec![
                new_expr_with_children(vec![new_text("Hello,".to_string())]),
                new_expr_with_children(vec![new_text("World,".to_string())]),
            ]),
            new_stmt_with_children(vec![new_curly_with_children(vec![
                new_ident(vec![
                    new_text("std".to_string()),
                    new_text("**".to_string()),
                ]),
                new_expr_with_children(vec![new_text("Contact".to_string())]),
            ])]),
            new_stmt_with_children(vec![new_expr_with_children(vec![
                new_square_with_children(vec![
                    new_ident(vec![new_text("std".to_string()), new_text("@".to_string())]),
                    new_expr_with_children(vec![new_text("My website".to_string())]),
                    new_expr_with_children(vec![new_text("https://example.com/".to_string())]),
                ]),
            ])]),
            new_stmt_with_children(vec![new_expr_with_children(vec![new_text(
                "2023.12.28".to_string(),
            )])]),
        ]);
        assert_ast_eq(&parsed, &expected);
        Ok(())
    }
}