1use super::{CompletedMarker, EXPR_RECOVERY, ITEM_RECOVERY, PARAM_RECOVERY, Parser, TYPE_RECOVERY};
28use crate::syntax_kind::SyntaxKind::{self, *};
29
30impl Parser<'_, '_> {
31 const FIELD_RECOVERY: &'static [SyntaxKind] = &[COMMA, R_BRACE, KW_PUBLIC, KW_PRIVATE, KW_CONSTANT];
33 const MODULE_ITEM_RECOVERY: &'static [SyntaxKind] = &[KW_CONST, KW_STRUCT, KW_FN, KW_FINAL, AT];
35 const PROGRAM_ITEM_EXPECTED: &'static [SyntaxKind] = &[
37 R_BRACE,
38 AT,
39 KW_RECORD,
40 KW_STRUCT,
41 KW_FN,
42 KW_FINAL,
43 KW_CONST,
44 KW_MAPPING,
45 KW_STORAGE,
46 KW_SCRIPT,
47 KW_INTERFACE,
48 ];
49 const RETURN_TYPE_RECOVERY: &'static [SyntaxKind] = &[COMMA, R_PAREN, L_BRACE];
51 const STRUCT_NAME_RECOVERY: &'static [SyntaxKind] = &[
53 L_BRACE,
54 R_BRACE,
55 SEMICOLON,
56 KW_IMPORT,
57 KW_PROGRAM,
58 KW_CONST,
59 KW_STRUCT,
60 KW_RECORD,
61 KW_FN,
62 KW_FINAL,
63 KW_MAPPING,
64 KW_STORAGE,
65 KW_SCRIPT,
66 KW_INTERFACE,
67 AT,
68 ];
69
70 fn eat_visibility(&mut self) -> Option<SyntaxKind> {
73 if self.eat(KW_PUBLIC) {
74 Some(KW_PUBLIC)
75 } else if self.eat(KW_PRIVATE) {
76 Some(KW_PRIVATE)
77 } else if self.eat(KW_CONSTANT) {
78 Some(KW_CONSTANT)
79 } else {
80 None
81 }
82 }
83
84 pub fn parse_file_items(&mut self) {
97 loop {
98 self.skip_trivia();
99 if self.at_eof() {
100 break;
101 }
102
103 self.erroring = false;
105
106 match self.current() {
107 KW_IMPORT => {
108 self.parse_import();
109 }
110 KW_PROGRAM => {
111 self.parse_program_decl();
112 }
113 KW_CONST | KW_STRUCT | KW_FN | KW_FINAL | AT => {
116 if self.parse_module_item().is_none() {
117 self.error_and_bump("expected module item");
118 }
119 }
120 KW_INTERFACE => {
121 self.parse_interface_def();
122 }
123 KW_VIEW => {
127 if self.parse_module_item().is_none() {
128 self.error_and_bump("expected module item");
129 }
130 }
131 _ => {
132 self.error("expected `import`, `program`, or module item at top level");
133 self.recover(&[
134 KW_IMPORT,
135 KW_PROGRAM,
136 KW_CONST,
137 KW_STRUCT,
138 KW_FN,
139 KW_FINAL,
140 KW_VIEW,
141 KW_INTERFACE,
142 AT,
143 ]);
144 }
145 }
146 }
147 }
148
149 pub fn parse_module_items(&mut self) {
154 loop {
155 self.skip_trivia();
156 if self.at_eof() {
157 break;
158 }
159
160 self.erroring = false;
162
163 if self.parse_module_item().is_none() {
164 self.error("expected `const`, `struct`, or `fn` in module");
165 self.recover(Self::MODULE_ITEM_RECOVERY);
166 }
167 }
168 }
169
170 fn parse_module_item(&mut self) -> Option<CompletedMarker> {
174 match self.current() {
175 KW_CONST => self.parse_global_const(),
176 KW_STRUCT => self.parse_composite_def(STRUCT_DEF),
177 KW_INTERFACE => self.parse_interface_def(),
178 AT | KW_FN | KW_FINAL | KW_VIEW => self.parse_function_or_constructor(),
179 _ => None,
180 }
181 }
182
183 fn parse_import(&mut self) -> Option<CompletedMarker> {
185 let m = self.start();
186 self.bump_any(); self.parse_program_id();
190
191 self.expect(SEMICOLON);
192 Some(m.complete(self, IMPORT))
193 }
194
195 fn parse_program_decl(&mut self) -> Option<CompletedMarker> {
197 let m = self.start();
198 self.bump_any(); self.parse_program_id();
202
203 self.skip_trivia();
204 if self.eat(COLON) {
206 self.skip_trivia();
207 self.erroring = false;
209 self.parse_parent_list();
210 }
211
212 self.expect(L_BRACE);
213
214 while !self.at(R_BRACE) && !self.at_eof() {
216 self.erroring = false;
218 if self.parse_program_item().is_none() {
219 self.recover(ITEM_RECOVERY);
221 }
222 }
223
224 self.expect(R_BRACE);
225 Some(m.complete(self, PROGRAM_DECL))
226 }
227
228 fn parse_program_id(&mut self) {
230 self.skip_trivia();
231 if self.at(IDENT) {
232 self.bump_any(); self.expect(DOT);
234 if !self.eat(KW_ALEO) {
235 if self.at(IDENT) {
236 self.bump_any();
239 } else {
240 self.error("expected 'aleo'");
241 }
242 }
243 } else {
244 self.error("expected program name");
245 }
246 }
247
248 fn parse_program_item(&mut self) -> Option<CompletedMarker> {
253 match self.current() {
257 AT => self.parse_function_or_constructor(),
258 KW_STRUCT => self.parse_composite_def(STRUCT_DEF),
259 KW_RECORD => self.parse_composite_def(RECORD_DEF),
260 KW_MAPPING => self.parse_mapping_def(),
261 KW_STORAGE => self.parse_storage_def(),
262 KW_CONST => self.parse_global_const(),
263 KW_FN | KW_FINAL | KW_VIEW | KW_CONSTRUCTOR => self.parse_function_or_constructor(),
264 KW_SCRIPT => {
265 self.error("'script' functions are no longer supported; use @test on entry point functions instead");
266 self.bump_any();
267 None
268 }
269 KW_INTERFACE => self.parse_interface_def(),
270 _ => {
271 let expected: Vec<&str> = Self::PROGRAM_ITEM_EXPECTED.iter().map(|k| k.user_friendly_name()).collect();
272 self.error_unexpected(self.current(), &expected);
273 None
274 }
275 }
276 }
277
278 fn parse_annotation(&mut self) {
280 let m = self.start();
281 self.bump_any(); if self.current_including_trivia().is_trivia() {
285 self.error_unexpected(self.current(), &["an identifier", "'program'"]);
286 self.skip_trivia();
287 if self.at(IDENT) || self.current().is_keyword() {
289 self.bump_any();
290 }
291 } else if self.at(IDENT) || self.current().is_keyword() {
292 self.bump_any();
295 } else {
296 self.error("expected annotation name");
297 }
298
299 if self.eat(L_PAREN) {
302 if !self.at(R_PAREN) {
303 self.parse_annotation_member();
304 while self.eat(COMMA) {
305 if self.at(R_PAREN) {
306 break;
307 }
308 self.erroring = false;
310 self.parse_annotation_member();
311 }
312 }
313 if !self.at(R_PAREN) && !self.at_eof() {
315 self.error_unexpected(self.current(), &["')'", "','"]);
316 while !self.at(R_PAREN) && !self.at_eof() {
318 self.bump_any();
319 }
320 }
321 self.expect(R_PAREN);
322 }
323
324 m.complete(self, ANNOTATION);
325 }
326
327 fn parse_annotation_member(&mut self) {
329 let m = self.start();
330 if self.at(IDENT) || self.at(KW_ADDRESS) || self.at(KW_MAPPING) {
332 self.bump_any();
333 } else {
334 self.error_unexpected(self.current(), &["an identifier", "')'", "'address", "'mapping'"]);
335 while !self.at(COMMA) && !self.at(R_PAREN) && !self.at_eof() {
337 self.bump_any();
338 }
339 m.abandon(self);
340 return;
341 }
342 self.expect(EQ);
343 if self.at(STRING) {
344 self.bump_any();
345 } else {
346 self.error("expected string literal for annotation value");
347 }
348 m.complete(self, ANNOTATION_PAIR);
349 }
350
351 fn parse_composite_def(&mut self, kind: SyntaxKind) -> Option<CompletedMarker> {
353 let m = self.start();
354 let label = if kind == STRUCT_DEF { "struct" } else { "record" };
355 self.bump_any(); self.skip_trivia();
359 if self.at(IDENT) {
360 self.bump_any();
361 } else {
362 self.error(format!("expected {label} name"));
363 self.recover(Self::STRUCT_NAME_RECOVERY);
364 return Some(m.complete(self, ERROR));
365 }
366
367 if self.at(COLON_COLON) && self.nth(1) == L_BRACKET {
369 self.bump_any(); self.parse_const_param_list();
371 }
372
373 self.expect(L_BRACE);
375 self.parse_struct_fields_until(&[R_BRACE]);
376 self.expect(R_BRACE);
377
378 Some(m.complete(self, kind))
379 }
380
381 fn parse_struct_fields_until(&mut self, closing_symbols: &[SyntaxKind]) {
383 while !closing_symbols.iter().any(|closing| self.at(*closing)) && !self.at_eof() {
384 self.skip_trivia();
386 let m = self.start();
387
388 let vis = self.eat_visibility();
390
391 self.skip_trivia();
393 if self.at(IDENT) {
394 self.bump_any();
395 } else {
396 m.abandon(self);
397 if !closing_symbols.iter().any(|closing| self.at(*closing)) {
399 self.error_recover("expected field name", Self::FIELD_RECOVERY);
400 }
401 if self.eat(COMMA) {
403 continue;
404 }
405 break;
406 }
407
408 self.expect(COLON);
410 if self.parse_type().is_none() {
411 self.error_recover("expected type", Self::FIELD_RECOVERY);
412 }
413
414 let kind = match vis {
415 Some(KW_PUBLIC) => STRUCT_MEMBER_PUBLIC,
416 Some(KW_PRIVATE) => STRUCT_MEMBER_PRIVATE,
417 Some(KW_CONSTANT) => STRUCT_MEMBER_CONSTANT,
418 _ => STRUCT_MEMBER,
419 };
420 m.complete(self, kind);
421
422 if !self.eat(COMMA) {
424 if !closing_symbols.iter().any(|closing| self.at(*closing)) && !self.at_eof() {
428 self.error("expected ','");
429 self.erroring = false;
431 }
432 }
433 }
434 }
435
436 fn parse_mapping_def(&mut self) -> Option<CompletedMarker> {
438 let m = self.start();
439 self.bump_any(); self.skip_trivia();
443 if self.at(IDENT) {
444 self.bump_any();
445 } else {
446 self.error("expected mapping name");
447 }
448
449 self.expect(COLON);
451 if self.parse_type().is_none() {
452 self.error_recover("expected key type", TYPE_RECOVERY);
453 }
454 self.expect(FAT_ARROW);
455 if self.parse_type().is_none() {
456 self.error_recover("expected value type", TYPE_RECOVERY);
457 }
458
459 self.expect(SEMICOLON);
460 Some(m.complete(self, MAPPING_DEF))
461 }
462
463 fn parse_storage_def(&mut self) -> Option<CompletedMarker> {
465 let m = self.start();
466 self.bump_any(); self.skip_trivia();
470 if self.at(IDENT) {
471 self.bump_any();
472 } else {
473 self.error("expected storage name");
474 }
475
476 self.expect(COLON);
478 if self.parse_type().is_none() {
479 self.error_recover("expected type", TYPE_RECOVERY);
480 }
481
482 self.expect(SEMICOLON);
483 Some(m.complete(self, STORAGE_DEF))
484 }
485
486 fn parse_global_const(&mut self) -> Option<CompletedMarker> {
488 let m = self.start();
489 self.bump_any(); self.skip_trivia();
493 if self.at(IDENT) {
494 self.bump_any();
495 } else {
496 self.error("expected constant name");
497 self.recover(&[SEMICOLON]);
498 self.eat(SEMICOLON);
499 return Some(m.complete(self, GLOBAL_CONST));
500 }
501
502 self.expect(COLON);
504 if self.parse_type().is_none() {
505 self.error_recover("expected type", TYPE_RECOVERY);
506 }
507
508 self.expect(EQ);
510 if self.parse_expr().is_none() {
511 self.error_recover("expected expression", EXPR_RECOVERY);
512 }
513
514 self.expect(SEMICOLON);
515 Some(m.complete(self, GLOBAL_CONST))
516 }
517
518 fn parse_function_or_constructor(&mut self) -> Option<CompletedMarker> {
522 let m = self.start();
523
524 while self.at(AT) {
526 self.parse_annotation();
527 }
528
529 let ate_final = self.eat(KW_FINAL);
533 let ate_view = if ate_final && self.at(KW_VIEW) {
534 self.error("`final` and `view` cannot be combined");
535 self.bump_any();
536 false
537 } else {
538 self.eat(KW_VIEW)
539 };
540
541 match self.current() {
543 KW_FN => {
544 self.parse_function_body();
545 let kind = match (ate_final, ate_view) {
546 (true, _) => FINAL_FN_DEF,
547 (false, true) => VIEW_FN_DEF,
548 (false, false) => FUNCTION_DEF,
549 };
550 Some(m.complete(self, kind))
551 }
552 KW_CONSTRUCTOR => {
553 self.parse_constructor_body();
554 Some(m.complete(self, CONSTRUCTOR_DEF))
555 }
556 _ => {
557 self.error("expected 'fn' or 'constructor'");
558 m.abandon(self);
559 None
560 }
561 }
562 }
563
564 fn parse_function_body(&mut self) {
566 if !self.eat(KW_FN) {
568 self.error("expected 'fn'");
569 }
570
571 self.skip_trivia();
573 if self.at(IDENT) {
574 self.bump_any();
575 } else {
576 self.error("expected function name");
577 }
578
579 if self.at(COLON_COLON) && self.nth(1) == L_BRACKET {
581 self.bump_any(); self.parse_const_param_list();
583 }
584
585 self.parse_param_list();
587
588 if self.eat(ARROW) {
590 self.parse_return_type();
591 }
592
593 self.parse_block();
595 }
596
597 fn parse_return_type(&mut self) {
603 self.skip_trivia();
604 if self.at(L_PAREN) {
605 let m = self.start();
607 self.bump_any(); if !self.at(R_PAREN) {
609 self.eat_visibility();
611 if self.parse_type().is_none() {
612 self.error_recover("expected return type", Self::RETURN_TYPE_RECOVERY);
613 }
614 while self.eat(COMMA) {
615 if self.at(R_PAREN) {
616 break;
617 }
618 self.eat_visibility();
619 if self.parse_type().is_none() {
620 self.error_recover("expected return type", Self::RETURN_TYPE_RECOVERY);
621 }
622 }
623 }
624 self.expect(R_PAREN);
625 m.complete(self, RETURN_TYPE);
626 } else {
627 self.eat_visibility();
629 if self.parse_type().is_none() {
630 self.error_recover("expected return type", Self::RETURN_TYPE_RECOVERY);
631 }
632 }
633 }
634
635 fn parse_constructor_body(&mut self) {
637 self.bump_any(); self.parse_param_list();
641
642 self.parse_block();
644 }
645
646 fn parse_param_list(&mut self) {
648 let m = self.start();
649 self.expect(L_PAREN);
650
651 while !self.at(R_PAREN) && !self.at_eof() {
652 self.erroring = false;
654 self.parse_param();
655 if !self.eat(COMMA) {
656 break;
657 }
658 }
659
660 self.expect(R_PAREN);
661 m.complete(self, PARAM_LIST);
662 }
663
664 fn parse_parent_list(&mut self) {
666 let m = self.start();
667 self.parse_type();
668 self.skip_trivia();
669
670 while self.at(PLUS) && !self.at_eof() {
671 self.bump_any(); self.skip_trivia();
673 self.parse_type();
674 self.skip_trivia();
675 }
676
677 m.complete(self, PARENT_LIST);
678 }
679
680 fn parse_param(&mut self) {
682 let m = self.start();
683 self.skip_trivia();
684
685 let vis = self.eat_visibility();
687
688 self.skip_trivia();
690 if self.at(IDENT) {
691 self.bump_any();
692 } else {
693 self.error("expected parameter name");
694 }
695
696 self.expect(COLON);
698 if self.parse_type().is_none() {
699 self.error_recover("expected parameter type", PARAM_RECOVERY);
700 }
701
702 let kind = match vis {
703 Some(KW_PUBLIC) => PARAM_PUBLIC,
704 Some(KW_PRIVATE) => PARAM_PRIVATE,
705 Some(KW_CONSTANT) => PARAM_CONSTANT,
706 _ => PARAM,
707 };
708 m.complete(self, kind);
709 }
710
711 fn parse_interface_def(&mut self) -> Option<CompletedMarker> {
717 let m = self.start();
718 self.bump_any(); self.skip_trivia();
722 if self.at(IDENT) {
723 self.bump_any();
724 } else {
725 self.error("expected parameter name");
726 }
727
728 self.skip_trivia();
730 if self.eat(COLON) {
731 self.skip_trivia();
732 self.parse_parent_list();
733 }
734
735 self.expect(L_BRACE);
737 while !self.at(R_BRACE) && !self.at_eof() {
738 self.erroring = false;
739 if !self.parse_interface_item() {
740 self.error_and_bump("expected function or record prototype");
741 }
742 }
743 self.expect(R_BRACE);
744
745 Some(m.complete(self, INTERFACE_DEF))
746 }
747
748 fn parse_interface_item(&mut self) -> bool {
750 match self.current() {
751 KW_FN | KW_VIEW => {
752 self.parse_fn_prototype();
753 true
754 }
755 KW_RECORD => {
756 self.parse_record_prototype();
757 true
758 }
759 KW_MAPPING => {
760 self.parse_mapping_def();
761 true
762 }
763 KW_STORAGE => {
764 self.parse_storage_def();
765 true
766 }
767 _ => false,
768 }
769 }
770
771 fn parse_fn_prototype(&mut self) -> Option<CompletedMarker> {
776 let m = self.start();
777 let _ = self.eat(KW_VIEW);
778 self.bump_any(); self.skip_trivia();
782 if self.at(IDENT) {
783 self.bump_any();
784 } else {
785 self.error("expected function name");
786 }
787
788 if self.at(COLON_COLON) && self.nth(1) == L_BRACKET {
790 self.bump_any(); self.parse_const_param_list();
792 }
793
794 self.parse_param_list();
796
797 if self.eat(ARROW) {
799 self.parse_return_type();
800 }
801
802 self.expect(SEMICOLON);
803 Some(m.complete(self, FN_PROTOTYPE_DEF))
804 }
805
806 fn parse_record_prototype(&mut self) -> Option<CompletedMarker> {
808 let m = self.start();
809 self.bump_any(); self.skip_trivia();
812 if self.at(IDENT) {
813 self.bump_any();
814 } else {
815 self.error("expected record name");
816 }
817
818 if self.at(SEMICOLON) {
819 self.bump_any();
821 } else {
822 self.expect(L_BRACE);
824 self.parse_struct_fields_until(&[DOT_DOT, R_BRACE]);
825 if self.eat(DOT_DOT) {
826 self.expect(R_BRACE);
827 } else {
828 self.error("expected `..`; fully constraining record fields in interfaces is not yet supported");
829 self.expect(R_BRACE);
830 }
831 }
832
833 Some(m.complete(self, RECORD_PROTOTYPE_DEF))
834 }
835}
836
837#[cfg(test)]
838mod tests {
839 use super::*;
840 use crate::{lexer::lex, parser::Parse};
841 use expect_test::{Expect, expect};
842
843 fn check_file(input: &str, expect: Expect) {
844 let (tokens, _) = lex(input);
845 let mut parser = Parser::new(input, &tokens);
846 let root = parser.start();
847 parser.parse_file_items();
848 root.complete(&mut parser, ROOT);
849 let parse: Parse = parser.finish(vec![]);
850 let output = format!("{:#?}", parse.syntax());
851 expect.assert_eq(&output);
852 }
853
854 fn check_file_no_errors(input: &str) {
855 let (tokens, _) = lex(input);
856 let mut parser = Parser::new(input, &tokens);
857 let root = parser.start();
858 parser.parse_file_items();
859 root.complete(&mut parser, ROOT);
860 let parse: Parse = parser.finish(vec![]);
861 if !parse.errors().is_empty() {
862 for err in parse.errors() {
863 eprintln!("error at {:?}: {}", err.range, err.message);
864 }
865 eprintln!("tree:\n{:#?}", parse.syntax());
866 panic!("parse had {} error(s)", parse.errors().len());
867 }
868 }
869
870 #[test]
875 fn parse_import() {
876 check_file("import credits.aleo;", expect![[r#"
877 ROOT@0..20
878 IMPORT@0..20
879 KW_IMPORT@0..6 "import"
880 WHITESPACE@6..7 " "
881 IDENT@7..14 "credits"
882 DOT@14..15 "."
883 KW_ALEO@15..19 "aleo"
884 SEMICOLON@19..20 ";"
885 "#]]);
886 }
887
888 #[test]
893 fn parse_program_empty() {
894 check_file("program test.aleo { }", expect![[r#"
895 ROOT@0..21
896 PROGRAM_DECL@0..21
897 KW_PROGRAM@0..7 "program"
898 WHITESPACE@7..8 " "
899 IDENT@8..12 "test"
900 DOT@12..13 "."
901 KW_ALEO@13..17 "aleo"
902 WHITESPACE@17..18 " "
903 L_BRACE@18..19 "{"
904 WHITESPACE@19..20 " "
905 R_BRACE@20..21 "}"
906 "#]]);
907 }
908
909 #[test]
914 fn parse_struct() {
915 check_file("program test.aleo { struct Point { x: u32, y: u32 } }", expect![[r#"
916 ROOT@0..53
917 PROGRAM_DECL@0..53
918 KW_PROGRAM@0..7 "program"
919 WHITESPACE@7..8 " "
920 IDENT@8..12 "test"
921 DOT@12..13 "."
922 KW_ALEO@13..17 "aleo"
923 WHITESPACE@17..18 " "
924 L_BRACE@18..19 "{"
925 STRUCT_DEF@19..51
926 WHITESPACE@19..20 " "
927 KW_STRUCT@20..26 "struct"
928 WHITESPACE@26..27 " "
929 IDENT@27..32 "Point"
930 WHITESPACE@32..33 " "
931 L_BRACE@33..34 "{"
932 WHITESPACE@34..35 " "
933 STRUCT_MEMBER@35..41
934 IDENT@35..36 "x"
935 COLON@36..37 ":"
936 WHITESPACE@37..38 " "
937 TYPE_PRIMITIVE@38..41
938 KW_U32@38..41 "u32"
939 COMMA@41..42 ","
940 WHITESPACE@42..43 " "
941 STRUCT_MEMBER@43..49
942 IDENT@43..44 "y"
943 COLON@44..45 ":"
944 WHITESPACE@45..46 " "
945 TYPE_PRIMITIVE@46..49
946 KW_U32@46..49 "u32"
947 WHITESPACE@49..50 " "
948 R_BRACE@50..51 "}"
949 WHITESPACE@51..52 " "
950 R_BRACE@52..53 "}"
951 "#]]);
952 }
953
954 #[test]
959 fn parse_record() {
960 check_file("program test.aleo { record Token { owner: address, amount: u64 } }", expect![[r#"
961 ROOT@0..66
962 PROGRAM_DECL@0..66
963 KW_PROGRAM@0..7 "program"
964 WHITESPACE@7..8 " "
965 IDENT@8..12 "test"
966 DOT@12..13 "."
967 KW_ALEO@13..17 "aleo"
968 WHITESPACE@17..18 " "
969 L_BRACE@18..19 "{"
970 RECORD_DEF@19..64
971 WHITESPACE@19..20 " "
972 KW_RECORD@20..26 "record"
973 WHITESPACE@26..27 " "
974 IDENT@27..32 "Token"
975 WHITESPACE@32..33 " "
976 L_BRACE@33..34 "{"
977 WHITESPACE@34..35 " "
978 STRUCT_MEMBER@35..49
979 IDENT@35..40 "owner"
980 COLON@40..41 ":"
981 WHITESPACE@41..42 " "
982 TYPE_PRIMITIVE@42..49
983 KW_ADDRESS@42..49 "address"
984 COMMA@49..50 ","
985 WHITESPACE@50..51 " "
986 STRUCT_MEMBER@51..62
987 IDENT@51..57 "amount"
988 COLON@57..58 ":"
989 WHITESPACE@58..59 " "
990 TYPE_PRIMITIVE@59..62
991 KW_U64@59..62 "u64"
992 WHITESPACE@62..63 " "
993 R_BRACE@63..64 "}"
994 WHITESPACE@64..65 " "
995 R_BRACE@65..66 "}"
996 "#]]);
997 }
998
999 #[test]
1004 fn parse_mapping() {
1005 check_file("program test.aleo { mapping balances: address => u64; }", expect![[r#"
1006 ROOT@0..55
1007 PROGRAM_DECL@0..55
1008 KW_PROGRAM@0..7 "program"
1009 WHITESPACE@7..8 " "
1010 IDENT@8..12 "test"
1011 DOT@12..13 "."
1012 KW_ALEO@13..17 "aleo"
1013 WHITESPACE@17..18 " "
1014 L_BRACE@18..19 "{"
1015 MAPPING_DEF@19..53
1016 WHITESPACE@19..20 " "
1017 KW_MAPPING@20..27 "mapping"
1018 WHITESPACE@27..28 " "
1019 IDENT@28..36 "balances"
1020 COLON@36..37 ":"
1021 WHITESPACE@37..38 " "
1022 TYPE_PRIMITIVE@38..45
1023 KW_ADDRESS@38..45 "address"
1024 WHITESPACE@45..46 " "
1025 FAT_ARROW@46..48 "=>"
1026 WHITESPACE@48..49 " "
1027 TYPE_PRIMITIVE@49..52
1028 KW_U64@49..52 "u64"
1029 SEMICOLON@52..53 ";"
1030 WHITESPACE@53..54 " "
1031 R_BRACE@54..55 "}"
1032 "#]]);
1033 }
1034
1035 #[test]
1040 fn parse_function() {
1041 check_file("program test.aleo { fn add(a: u32, b: u32) -> u32 { return a + b; } }", expect![[r#"
1042 ROOT@0..69
1043 PROGRAM_DECL@0..69
1044 KW_PROGRAM@0..7 "program"
1045 WHITESPACE@7..8 " "
1046 IDENT@8..12 "test"
1047 DOT@12..13 "."
1048 KW_ALEO@13..17 "aleo"
1049 WHITESPACE@17..18 " "
1050 L_BRACE@18..19 "{"
1051 FUNCTION_DEF@19..67
1052 WHITESPACE@19..20 " "
1053 KW_FN@20..22 "fn"
1054 WHITESPACE@22..23 " "
1055 IDENT@23..26 "add"
1056 PARAM_LIST@26..42
1057 L_PAREN@26..27 "("
1058 PARAM@27..33
1059 IDENT@27..28 "a"
1060 COLON@28..29 ":"
1061 WHITESPACE@29..30 " "
1062 TYPE_PRIMITIVE@30..33
1063 KW_U32@30..33 "u32"
1064 COMMA@33..34 ","
1065 PARAM@34..41
1066 WHITESPACE@34..35 " "
1067 IDENT@35..36 "b"
1068 COLON@36..37 ":"
1069 WHITESPACE@37..38 " "
1070 TYPE_PRIMITIVE@38..41
1071 KW_U32@38..41 "u32"
1072 R_PAREN@41..42 ")"
1073 WHITESPACE@42..43 " "
1074 ARROW@43..45 "->"
1075 WHITESPACE@45..46 " "
1076 TYPE_PRIMITIVE@46..49
1077 KW_U32@46..49 "u32"
1078 BLOCK@49..67
1079 WHITESPACE@49..50 " "
1080 L_BRACE@50..51 "{"
1081 WHITESPACE@51..52 " "
1082 RETURN_STMT@52..65
1083 KW_RETURN@52..58 "return"
1084 WHITESPACE@58..59 " "
1085 BINARY_EXPR@59..64
1086 PATH_EXPR@59..61
1087 IDENT@59..60 "a"
1088 WHITESPACE@60..61 " "
1089 PLUS@61..62 "+"
1090 WHITESPACE@62..63 " "
1091 PATH_EXPR@63..64
1092 IDENT@63..64 "b"
1093 SEMICOLON@64..65 ";"
1094 WHITESPACE@65..66 " "
1095 R_BRACE@66..67 "}"
1096 WHITESPACE@67..68 " "
1097 R_BRACE@68..69 "}"
1098 "#]]);
1099 }
1100
1101 #[test]
1102 fn parse_final_function() {
1103 check_file("program test.aleo { } final fn foo() { assert_eq(1u64, 1u64); }", expect![[r#"
1104 ROOT@0..63
1105 PROGRAM_DECL@0..21
1106 KW_PROGRAM@0..7 "program"
1107 WHITESPACE@7..8 " "
1108 IDENT@8..12 "test"
1109 DOT@12..13 "."
1110 KW_ALEO@13..17 "aleo"
1111 WHITESPACE@17..18 " "
1112 L_BRACE@18..19 "{"
1113 WHITESPACE@19..20 " "
1114 R_BRACE@20..21 "}"
1115 WHITESPACE@21..22 " "
1116 FINAL_FN_DEF@22..63
1117 KW_FINAL@22..27 "final"
1118 WHITESPACE@27..28 " "
1119 KW_FN@28..30 "fn"
1120 WHITESPACE@30..31 " "
1121 IDENT@31..34 "foo"
1122 PARAM_LIST@34..36
1123 L_PAREN@34..35 "("
1124 R_PAREN@35..36 ")"
1125 WHITESPACE@36..37 " "
1126 BLOCK@37..63
1127 L_BRACE@37..38 "{"
1128 WHITESPACE@38..39 " "
1129 ASSERT_EQ_STMT@39..61
1130 KW_ASSERT_EQ@39..48 "assert_eq"
1131 L_PAREN@48..49 "("
1132 LITERAL_INT@49..53
1133 INTEGER@49..53 "1u64"
1134 COMMA@53..54 ","
1135 WHITESPACE@54..55 " "
1136 LITERAL_INT@55..59
1137 INTEGER@55..59 "1u64"
1138 R_PAREN@59..60 ")"
1139 SEMICOLON@60..61 ";"
1140 WHITESPACE@61..62 " "
1141 R_BRACE@62..63 "}"
1142 "#]]);
1143 }
1144 #[test]
1149 fn parse_view_function() {
1150 check_file_no_errors(
1151 "program test.aleo { mapping balances: address => u64; view fn balance_of(addr: address) -> u64 { return 0u64; } }",
1152 );
1153 }
1154
1155 #[test]
1156 fn parse_view_uses_view_node_kind() {
1157 check_file("program test.aleo { view fn q() -> u64 { return 0u64; } }", expect![[r#"
1158 ROOT@0..57
1159 PROGRAM_DECL@0..57
1160 KW_PROGRAM@0..7 "program"
1161 WHITESPACE@7..8 " "
1162 IDENT@8..12 "test"
1163 DOT@12..13 "."
1164 KW_ALEO@13..17 "aleo"
1165 WHITESPACE@17..18 " "
1166 L_BRACE@18..19 "{"
1167 VIEW_FN_DEF@19..55
1168 WHITESPACE@19..20 " "
1169 KW_VIEW@20..24 "view"
1170 WHITESPACE@24..25 " "
1171 KW_FN@25..27 "fn"
1172 WHITESPACE@27..28 " "
1173 IDENT@28..29 "q"
1174 PARAM_LIST@29..31
1175 L_PAREN@29..30 "("
1176 R_PAREN@30..31 ")"
1177 WHITESPACE@31..32 " "
1178 ARROW@32..34 "->"
1179 WHITESPACE@34..35 " "
1180 TYPE_PRIMITIVE@35..38
1181 KW_U64@35..38 "u64"
1182 BLOCK@38..55
1183 WHITESPACE@38..39 " "
1184 L_BRACE@39..40 "{"
1185 WHITESPACE@40..41 " "
1186 RETURN_STMT@41..53
1187 KW_RETURN@41..47 "return"
1188 WHITESPACE@47..48 " "
1189 LITERAL_INT@48..52
1190 INTEGER@48..52 "0u64"
1191 SEMICOLON@52..53 ";"
1192 WHITESPACE@53..54 " "
1193 R_BRACE@54..55 "}"
1194 WHITESPACE@55..56 " "
1195 R_BRACE@56..57 "}"
1196 "#]]);
1197 }
1198
1199 #[test]
1200 fn parse_view_with_input() {
1201 check_file_no_errors("program test.aleo { view fn q(addr: address, k: u32) -> u64 { return 1u64; } }");
1202 }
1203
1204 #[test]
1205 fn parse_final_view_rejected() {
1206 let input = "program test.aleo { final view fn q() {} }";
1207 let (tokens, _) = lex(input);
1208 let mut parser = Parser::new(input, &tokens);
1209 let root = parser.start();
1210 parser.parse_file_items();
1211 root.complete(&mut parser, ROOT);
1212 let parse: Parse = parser.finish(vec![]);
1213 let errors = parse.errors();
1214 assert!(
1215 errors.iter().any(|e| e.message.contains("`final` and `view` cannot be combined")),
1216 "expected `final view` rejection, got: {errors:?}"
1217 );
1218 }
1219
1220 #[test]
1225 fn parse_function_const_generic_single() {
1226 check_file_no_errors("program test.aleo { fn foo::[N: u32]() {} }");
1227 }
1228
1229 #[test]
1230 fn parse_function_const_generic_multi() {
1231 check_file_no_errors("program test.aleo { fn bar::[N: u32, M: u32](arr: u32) -> u32 { return 0u32; } }");
1232 }
1233
1234 #[test]
1235 fn parse_function_const_generic_empty() {
1236 check_file_no_errors("program test.aleo { fn baz::[]() {} }");
1237 }
1238
1239 #[test]
1240 fn parse_final_entry_const_generic() {
1241 check_file_no_errors("program test.aleo { fn t::[N: u32]() -> Final { return final {}; } }");
1242 }
1243
1244 #[test]
1245 fn parse_struct_const_generic() {
1246 check_file_no_errors("program test.aleo { struct Foo::[N: u32] { arr: u32, } }");
1247 }
1248
1249 #[test]
1250 fn parse_struct_const_generic_multi() {
1251 check_file_no_errors("program test.aleo { struct Matrix::[M: u32, N: u32] { data: u32, } }");
1252 }
1253
1254 #[test]
1255 fn parse_record_const_generic() {
1256 check_file_no_errors("program test.aleo { record Bar::[N: u32] { owner: address, } }");
1258 }
1259
1260 #[test]
1261 fn parse_transition() {
1262 check_file("program test.aleo { fn main(public x: u32) { } }", expect![[r#"
1263 ROOT@0..48
1264 PROGRAM_DECL@0..48
1265 KW_PROGRAM@0..7 "program"
1266 WHITESPACE@7..8 " "
1267 IDENT@8..12 "test"
1268 DOT@12..13 "."
1269 KW_ALEO@13..17 "aleo"
1270 WHITESPACE@17..18 " "
1271 L_BRACE@18..19 "{"
1272 FUNCTION_DEF@19..46
1273 WHITESPACE@19..20 " "
1274 KW_FN@20..22 "fn"
1275 WHITESPACE@22..23 " "
1276 IDENT@23..27 "main"
1277 PARAM_LIST@27..42
1278 L_PAREN@27..28 "("
1279 PARAM_PUBLIC@28..41
1280 KW_PUBLIC@28..34 "public"
1281 WHITESPACE@34..35 " "
1282 IDENT@35..36 "x"
1283 COLON@36..37 ":"
1284 WHITESPACE@37..38 " "
1285 TYPE_PRIMITIVE@38..41
1286 KW_U32@38..41 "u32"
1287 R_PAREN@41..42 ")"
1288 WHITESPACE@42..43 " "
1289 BLOCK@43..46
1290 L_BRACE@43..44 "{"
1291 WHITESPACE@44..45 " "
1292 R_BRACE@45..46 "}"
1293 WHITESPACE@46..47 " "
1294 R_BRACE@47..48 "}"
1295 "#]]);
1296 }
1297
1298 #[test]
1303 fn parse_record_visibility() {
1304 check_file("program test.aleo { record Token { public owner: address, private amount: u64, } }", expect![[
1305 r#"
1306 ROOT@0..82
1307 PROGRAM_DECL@0..82
1308 KW_PROGRAM@0..7 "program"
1309 WHITESPACE@7..8 " "
1310 IDENT@8..12 "test"
1311 DOT@12..13 "."
1312 KW_ALEO@13..17 "aleo"
1313 WHITESPACE@17..18 " "
1314 L_BRACE@18..19 "{"
1315 RECORD_DEF@19..80
1316 WHITESPACE@19..20 " "
1317 KW_RECORD@20..26 "record"
1318 WHITESPACE@26..27 " "
1319 IDENT@27..32 "Token"
1320 WHITESPACE@32..33 " "
1321 L_BRACE@33..34 "{"
1322 WHITESPACE@34..35 " "
1323 STRUCT_MEMBER_PUBLIC@35..56
1324 KW_PUBLIC@35..41 "public"
1325 WHITESPACE@41..42 " "
1326 IDENT@42..47 "owner"
1327 COLON@47..48 ":"
1328 WHITESPACE@48..49 " "
1329 TYPE_PRIMITIVE@49..56
1330 KW_ADDRESS@49..56 "address"
1331 COMMA@56..57 ","
1332 WHITESPACE@57..58 " "
1333 STRUCT_MEMBER_PRIVATE@58..77
1334 KW_PRIVATE@58..65 "private"
1335 WHITESPACE@65..66 " "
1336 IDENT@66..72 "amount"
1337 COLON@72..73 ":"
1338 WHITESPACE@73..74 " "
1339 TYPE_PRIMITIVE@74..77
1340 KW_U64@74..77 "u64"
1341 COMMA@77..78 ","
1342 WHITESPACE@78..79 " "
1343 R_BRACE@79..80 "}"
1344 WHITESPACE@80..81 " "
1345 R_BRACE@81..82 "}"
1346 "#
1347 ]]);
1348 }
1349
1350 #[test]
1355 fn parse_final_fn_with_return() {
1356 check_file("program test.aleo { final fn main(public x: u32) -> Final { return final {}; } }", expect![[r#"
1357 ROOT@0..80
1358 PROGRAM_DECL@0..80
1359 KW_PROGRAM@0..7 "program"
1360 WHITESPACE@7..8 " "
1361 IDENT@8..12 "test"
1362 DOT@12..13 "."
1363 KW_ALEO@13..17 "aleo"
1364 WHITESPACE@17..18 " "
1365 L_BRACE@18..19 "{"
1366 FINAL_FN_DEF@19..78
1367 WHITESPACE@19..20 " "
1368 KW_FINAL@20..25 "final"
1369 WHITESPACE@25..26 " "
1370 KW_FN@26..28 "fn"
1371 WHITESPACE@28..29 " "
1372 IDENT@29..33 "main"
1373 PARAM_LIST@33..48
1374 L_PAREN@33..34 "("
1375 PARAM_PUBLIC@34..47
1376 KW_PUBLIC@34..40 "public"
1377 WHITESPACE@40..41 " "
1378 IDENT@41..42 "x"
1379 COLON@42..43 ":"
1380 WHITESPACE@43..44 " "
1381 TYPE_PRIMITIVE@44..47
1382 KW_U32@44..47 "u32"
1383 R_PAREN@47..48 ")"
1384 WHITESPACE@48..49 " "
1385 ARROW@49..51 "->"
1386 WHITESPACE@51..52 " "
1387 TYPE_FINAL@52..58
1388 KW_FINAL_UPPER@52..57 "Final"
1389 WHITESPACE@57..58 " "
1390 BLOCK@58..78
1391 L_BRACE@58..59 "{"
1392 WHITESPACE@59..60 " "
1393 RETURN_STMT@60..76
1394 KW_RETURN@60..66 "return"
1395 WHITESPACE@66..67 " "
1396 FINAL_EXPR@67..75
1397 KW_FINAL@67..72 "final"
1398 WHITESPACE@72..73 " "
1399 BLOCK@73..75
1400 L_BRACE@73..74 "{"
1401 R_BRACE@74..75 "}"
1402 SEMICOLON@75..76 ";"
1403 WHITESPACE@76..77 " "
1404 R_BRACE@77..78 "}"
1405 WHITESPACE@78..79 " "
1406 R_BRACE@79..80 "}"
1407 "#]]);
1408 }
1409
1410 #[test]
1415 fn parse_function_return_tuple() {
1416 check_file(
1417 "program test.aleo { fn foo() -> (public u32, private field) { return (1u32, 2field); } }",
1418 expect![[r#"
1419 ROOT@0..88
1420 PROGRAM_DECL@0..88
1421 KW_PROGRAM@0..7 "program"
1422 WHITESPACE@7..8 " "
1423 IDENT@8..12 "test"
1424 DOT@12..13 "."
1425 KW_ALEO@13..17 "aleo"
1426 WHITESPACE@17..18 " "
1427 L_BRACE@18..19 "{"
1428 FUNCTION_DEF@19..86
1429 WHITESPACE@19..20 " "
1430 KW_FN@20..22 "fn"
1431 WHITESPACE@22..23 " "
1432 IDENT@23..26 "foo"
1433 PARAM_LIST@26..28
1434 L_PAREN@26..27 "("
1435 R_PAREN@27..28 ")"
1436 WHITESPACE@28..29 " "
1437 ARROW@29..31 "->"
1438 WHITESPACE@31..32 " "
1439 RETURN_TYPE@32..59
1440 L_PAREN@32..33 "("
1441 KW_PUBLIC@33..39 "public"
1442 WHITESPACE@39..40 " "
1443 TYPE_PRIMITIVE@40..43
1444 KW_U32@40..43 "u32"
1445 COMMA@43..44 ","
1446 WHITESPACE@44..45 " "
1447 KW_PRIVATE@45..52 "private"
1448 WHITESPACE@52..53 " "
1449 TYPE_PRIMITIVE@53..58
1450 KW_FIELD@53..58 "field"
1451 R_PAREN@58..59 ")"
1452 BLOCK@59..86
1453 WHITESPACE@59..60 " "
1454 L_BRACE@60..61 "{"
1455 WHITESPACE@61..62 " "
1456 RETURN_STMT@62..84
1457 KW_RETURN@62..68 "return"
1458 WHITESPACE@68..69 " "
1459 TUPLE_EXPR@69..83
1460 L_PAREN@69..70 "("
1461 LITERAL_INT@70..74
1462 INTEGER@70..74 "1u32"
1463 COMMA@74..75 ","
1464 WHITESPACE@75..76 " "
1465 LITERAL_FIELD@76..82
1466 INTEGER@76..82 "2field"
1467 R_PAREN@82..83 ")"
1468 SEMICOLON@83..84 ";"
1469 WHITESPACE@84..85 " "
1470 R_BRACE@85..86 "}"
1471 WHITESPACE@86..87 " "
1472 R_BRACE@87..88 "}"
1473 "#]],
1474 );
1475 }
1476
1477 #[test]
1482 fn parse_function_multi_annotation() {
1483 check_file("program test.aleo { @test @foo(k = \"v\") fn bar() { } }", expect![[r#"
1484 ROOT@0..54
1485 PROGRAM_DECL@0..54
1486 KW_PROGRAM@0..7 "program"
1487 WHITESPACE@7..8 " "
1488 IDENT@8..12 "test"
1489 DOT@12..13 "."
1490 KW_ALEO@13..17 "aleo"
1491 WHITESPACE@17..18 " "
1492 L_BRACE@18..19 "{"
1493 FUNCTION_DEF@19..52
1494 ANNOTATION@19..26
1495 WHITESPACE@19..20 " "
1496 AT@20..21 "@"
1497 IDENT@21..25 "test"
1498 WHITESPACE@25..26 " "
1499 ANNOTATION@26..39
1500 AT@26..27 "@"
1501 IDENT@27..30 "foo"
1502 L_PAREN@30..31 "("
1503 ANNOTATION_PAIR@31..38
1504 IDENT@31..32 "k"
1505 WHITESPACE@32..33 " "
1506 EQ@33..34 "="
1507 WHITESPACE@34..35 " "
1508 STRING@35..38 "\"v\""
1509 R_PAREN@38..39 ")"
1510 WHITESPACE@39..40 " "
1511 KW_FN@40..42 "fn"
1512 WHITESPACE@42..43 " "
1513 IDENT@43..46 "bar"
1514 PARAM_LIST@46..48
1515 L_PAREN@46..47 "("
1516 R_PAREN@47..48 ")"
1517 WHITESPACE@48..49 " "
1518 BLOCK@49..52
1519 L_BRACE@49..50 "{"
1520 WHITESPACE@50..51 " "
1521 R_BRACE@51..52 "}"
1522 WHITESPACE@52..53 " "
1523 R_BRACE@53..54 "}"
1524 "#]]);
1525 }
1526
1527 #[test]
1532 fn parse_storage() {
1533 check_file("program test.aleo { storage state: u64; }", expect![[r#"
1534 ROOT@0..41
1535 PROGRAM_DECL@0..41
1536 KW_PROGRAM@0..7 "program"
1537 WHITESPACE@7..8 " "
1538 IDENT@8..12 "test"
1539 DOT@12..13 "."
1540 KW_ALEO@13..17 "aleo"
1541 WHITESPACE@17..18 " "
1542 L_BRACE@18..19 "{"
1543 STORAGE_DEF@19..39
1544 WHITESPACE@19..20 " "
1545 KW_STORAGE@20..27 "storage"
1546 WHITESPACE@27..28 " "
1547 IDENT@28..33 "state"
1548 COLON@33..34 ":"
1549 WHITESPACE@34..35 " "
1550 TYPE_PRIMITIVE@35..38
1551 KW_U64@35..38 "u64"
1552 SEMICOLON@38..39 ";"
1553 WHITESPACE@39..40 " "
1554 R_BRACE@40..41 "}"
1555 "#]]);
1556 }
1557
1558 #[test]
1563 fn parse_script_rejected() {
1564 check_file("program test.aleo { script main() { } }", expect![[r#"
1565 ROOT@0..39
1566 PROGRAM_DECL@0..39
1567 KW_PROGRAM@0..7 "program"
1568 WHITESPACE@7..8 " "
1569 IDENT@8..12 "test"
1570 DOT@12..13 "."
1571 KW_ALEO@13..17 "aleo"
1572 WHITESPACE@17..18 " "
1573 L_BRACE@18..19 "{"
1574 WHITESPACE@19..20 " "
1575 KW_SCRIPT@20..26 "script"
1576 ERROR@26..37
1577 WHITESPACE@26..27 " "
1578 IDENT@27..31 "main"
1579 L_PAREN@31..32 "("
1580 R_PAREN@32..33 ")"
1581 WHITESPACE@33..34 " "
1582 L_BRACE@34..35 "{"
1583 WHITESPACE@35..36 " "
1584 R_BRACE@36..37 "}"
1585 WHITESPACE@37..38 " "
1586 R_BRACE@38..39 "}"
1587 "#]]);
1588 }
1589
1590 #[test]
1595 fn parse_program_with_items() {
1596 check_file("program test.aleo { struct Foo { x: u32, } fn bar() -> u32 { return 1u32; } }", expect![[r#"
1597 ROOT@0..77
1598 PROGRAM_DECL@0..77
1599 KW_PROGRAM@0..7 "program"
1600 WHITESPACE@7..8 " "
1601 IDENT@8..12 "test"
1602 DOT@12..13 "."
1603 KW_ALEO@13..17 "aleo"
1604 WHITESPACE@17..18 " "
1605 L_BRACE@18..19 "{"
1606 STRUCT_DEF@19..42
1607 WHITESPACE@19..20 " "
1608 KW_STRUCT@20..26 "struct"
1609 WHITESPACE@26..27 " "
1610 IDENT@27..30 "Foo"
1611 WHITESPACE@30..31 " "
1612 L_BRACE@31..32 "{"
1613 WHITESPACE@32..33 " "
1614 STRUCT_MEMBER@33..39
1615 IDENT@33..34 "x"
1616 COLON@34..35 ":"
1617 WHITESPACE@35..36 " "
1618 TYPE_PRIMITIVE@36..39
1619 KW_U32@36..39 "u32"
1620 COMMA@39..40 ","
1621 WHITESPACE@40..41 " "
1622 R_BRACE@41..42 "}"
1623 FUNCTION_DEF@42..75
1624 WHITESPACE@42..43 " "
1625 KW_FN@43..45 "fn"
1626 WHITESPACE@45..46 " "
1627 IDENT@46..49 "bar"
1628 PARAM_LIST@49..51
1629 L_PAREN@49..50 "("
1630 R_PAREN@50..51 ")"
1631 WHITESPACE@51..52 " "
1632 ARROW@52..54 "->"
1633 WHITESPACE@54..55 " "
1634 TYPE_PRIMITIVE@55..58
1635 KW_U32@55..58 "u32"
1636 BLOCK@58..75
1637 WHITESPACE@58..59 " "
1638 L_BRACE@59..60 "{"
1639 WHITESPACE@60..61 " "
1640 RETURN_STMT@61..73
1641 KW_RETURN@61..67 "return"
1642 WHITESPACE@67..68 " "
1643 LITERAL_INT@68..72
1644 INTEGER@68..72 "1u32"
1645 SEMICOLON@72..73 ";"
1646 WHITESPACE@73..74 " "
1647 R_BRACE@74..75 "}"
1648 WHITESPACE@75..76 " "
1649 R_BRACE@76..77 "}"
1650 "#]]);
1651 }
1652
1653 #[test]
1658 fn parse_import_invalid_network() {
1659 check_file("import foo.bar;", expect![[r#"
1663 ROOT@0..15
1664 IMPORT@0..15
1665 KW_IMPORT@0..6 "import"
1666 WHITESPACE@6..7 " "
1667 IDENT@7..10 "foo"
1668 DOT@10..11 "."
1669 IDENT@11..14 "bar"
1670 SEMICOLON@14..15 ";"
1671 "#]]);
1672 }
1673
1674 #[test]
1679 fn parse_annotation_space_after_at() {
1680 let (tokens, _) = lex("program test.aleo { @ test fn foo() { } }");
1682 let mut parser = Parser::new("program test.aleo { @ test fn foo() { } }", &tokens);
1683 let root = parser.start();
1684 parser.parse_file_items();
1685 root.complete(&mut parser, ROOT);
1686 let parse: Parse = parser.finish(vec![]);
1687 assert!(!parse.errors().is_empty(), "expected error for space after @, got none");
1688 }
1689}