leo_parser_rowan/parser/
grammar.rs1use super::{Parse, Parser};
23use crate::{lexer::lex, syntax_kind::SyntaxKind::*};
24
25pub fn parse_file(source: &str) -> Parse {
29 let (tokens, lex_errors) = lex(source);
30 let mut parser = Parser::new(source, &tokens);
31
32 let root = parser.start();
33 parser.parse_file_items();
34 root.complete(&mut parser, ROOT);
35
36 parser.finish(lex_errors)
37}
38
39pub fn parse_expression_entry(source: &str) -> Parse {
41 let (tokens, lex_errors) = lex(source);
42 let mut parser = Parser::new(source, &tokens);
43
44 let root = parser.start();
45 let errors_before = parser.error_count();
46 parser.parse_expr();
47 if !parser.at_eof() && !parser.at(ERROR) && parser.error_count() == errors_before {
52 let expected: Vec<&str> = Parser::EXPR_CONTINUATION.iter().map(|k| k.user_friendly_name()).collect();
53 parser.error_unexpected(parser.current(), &expected);
54 }
55 root.complete(&mut parser, ROOT);
56
57 parser.finish(lex_errors)
58}
59
60pub fn parse_module_entry(source: &str) -> Parse {
62 let (tokens, lex_errors) = lex(source);
63 let mut parser = Parser::new(source, &tokens);
64
65 let root = parser.start();
66 parser.parse_module_items();
67 root.complete(&mut parser, ROOT);
68
69 parser.finish(lex_errors)
70}
71
72pub fn parse_statement_entry(source: &str) -> Parse {
74 let (tokens, lex_errors) = lex(source);
75 let mut parser = Parser::new(source, &tokens);
76
77 let root = parser.start();
78 let errors_before = parser.error_count();
79 parser.parse_stmt();
80 if !parser.at_eof() && !parser.at(ERROR) && parser.error_count() == errors_before {
84 let mut expected: Vec<&str> = Parser::EXPR_CONTINUATION.iter().map(|k| k.user_friendly_name()).collect();
85 expected.push("';'");
86 parser.error_unexpected(parser.current(), &expected);
87 }
88 root.complete(&mut parser, ROOT);
89
90 parser.finish(lex_errors)
91}
92
93#[cfg(test)]
94mod tests {
95 use super::*;
96 use expect_test::{Expect, expect};
97
98 fn check_file(input: &str, expect: Expect) {
99 let parse = parse_file(input);
100 let output = format!("{:#?}", parse.syntax());
101 expect.assert_eq(&output);
102 }
103
104 #[test]
105 fn parse_file_empty() {
106 check_file("", expect![[r#"
107 ROOT@0..0
108 "#]]);
109 }
110
111 #[test]
112 fn parse_file_trivial() {
113 check_file("program test.aleo { }", expect![[r#"
114 ROOT@0..21
115 PROGRAM_DECL@0..21
116 KW_PROGRAM@0..7 "program"
117 WHITESPACE@7..8 " "
118 IDENT@8..12 "test"
119 DOT@12..13 "."
120 KW_ALEO@13..17 "aleo"
121 WHITESPACE@17..18 " "
122 L_BRACE@18..19 "{"
123 WHITESPACE@19..20 " "
124 R_BRACE@20..21 "}"
125 "#]]);
126 }
127
128 fn check_module(input: &str, expect: Expect) {
129 let parse = parse_module_entry(input);
130 let output = format!("{:#?}", parse.syntax());
131 expect.assert_eq(&output);
132 }
133
134 fn check_module_no_errors(input: &str) {
135 let parse = parse_module_entry(input);
136 if !parse.errors().is_empty() {
137 for err in parse.errors() {
138 eprintln!("error at {:?}: {}", err.range, err.message);
139 }
140 eprintln!("tree:\n{:#?}", parse.syntax());
141 panic!("module parse had {} error(s)", parse.errors().len());
142 }
143 }
144
145 #[test]
146 fn parse_module_empty() {
147 check_module("", expect![[r#"
148 ROOT@0..0
149 "#]]);
150 }
151
152 #[test]
153 fn parse_module_const() {
154 check_module_no_errors("const X: u32 = 32u32;");
155 }
156
157 #[test]
158 fn parse_module_struct() {
159 check_module_no_errors("struct Data { values: u32, }");
160 }
161
162 #[test]
163 fn parse_module_inline_fn() {
164 check_module_no_errors("fn helper() -> u32 { return 0u32; }");
165 }
166
167 #[test]
168 fn parse_module_mixed_items() {
169 check_module_no_errors(
170 "const X: u32 = 3;\n\
171 struct Data { values: u32, }\n\
172 fn helper() -> u32 { return 0u32; }",
173 );
174 }
175
176 #[test]
177 fn parse_module_with_comments() {
178 check_module_no_errors(
181 "// --- Next Module: dep.leo --- //\n\
182 const X: u32 = 32u32;\n\
183 // --- Next Module: dep/inner.leo --- //\n\
184 const Y: u32 = 64u32;",
185 );
186 }
187
188 #[test]
193 fn parse_file_full() {
194 check_file("import credits.aleo;\nprogram test.aleo { fn main() { } }", expect![[r#"
195 ROOT@0..56
196 IMPORT@0..20
197 KW_IMPORT@0..6 "import"
198 WHITESPACE@6..7 " "
199 IDENT@7..14 "credits"
200 DOT@14..15 "."
201 KW_ALEO@15..19 "aleo"
202 SEMICOLON@19..20 ";"
203 LINEBREAK@20..21 "\n"
204 PROGRAM_DECL@21..56
205 KW_PROGRAM@21..28 "program"
206 WHITESPACE@28..29 " "
207 IDENT@29..33 "test"
208 DOT@33..34 "."
209 KW_ALEO@34..38 "aleo"
210 WHITESPACE@38..39 " "
211 L_BRACE@39..40 "{"
212 FUNCTION_DEF@40..54
213 WHITESPACE@40..41 " "
214 KW_FN@41..43 "fn"
215 WHITESPACE@43..44 " "
216 IDENT@44..48 "main"
217 PARAM_LIST@48..50
218 L_PAREN@48..49 "("
219 R_PAREN@49..50 ")"
220 WHITESPACE@50..51 " "
221 BLOCK@51..54
222 L_BRACE@51..52 "{"
223 WHITESPACE@52..53 " "
224 R_BRACE@53..54 "}"
225 WHITESPACE@54..55 " "
226 R_BRACE@55..56 "}"
227 "#]]);
228 }
229
230 #[test]
235 fn parse_module_recovery() {
236 let parse = parse_module_entry("42 struct Foo { x: u32, }");
240 assert!(!parse.errors().is_empty(), "expected errors from unrecognized token");
241 let tree = format!("{:#?}", parse.syntax());
242 assert!(tree.contains("STRUCT_DEF"), "expected struct to be recovered, got:\n{tree}");
243 }
244
245 #[test]
246 fn parse_file_with_module_sections() {
247 let source = "\
249program test.aleo {
250 fn foo() -> u32 { return 0u32; }
251}
252
253// --- Next Module: dep.leo --- //
254
255const X: u32 = 32u32;
256
257// --- Next Module: dep/inner.leo --- //
258
259const Y: u32 = 64u32;";
260
261 let parse = parse_file(source);
262 if !parse.errors().is_empty() {
263 for err in parse.errors() {
264 eprintln!("error at {:?}: {}", err.range, err.message);
265 }
266 eprintln!("tree:\n{:#?}", parse.syntax());
267 panic!("file parse had {} error(s)", parse.errors().len());
268 }
269 }
270}