vicuna_compiler/
parser.rs

1use crate::ast::{
2    BinaryOp, Expr, ExprBlock, ExprFields, Function, ImportType, MatchBindings, MatchCase, PostFix,
3    Program, Span, Stmt, TypeDeclaration, TypeFields, TypeSig, UnaryOp, Value,
4};
5use chumsky::prelude::*;
6use chumsky::primitive::OrderedContainer;
7use miette::Diagnostic;
8use serde::Serialize;
9use std::ops::Range;
10use thiserror::Error;
11
12#[derive(Debug, Clone, Serialize, Error, Diagnostic)]
13pub enum ParseError {
14    #[diagnostic(code(parse_error::expected_found))]
15    #[error("Expected {expected_chars:?} but found {received_char:?}")]
16    ExpectedFound {
17        #[label]
18        span: Range<usize>,
19        expected_chars: Vec<Option<char>>,
20        received_char: Option<char>,
21    },
22}
23
24impl chumsky::Error<char> for ParseError {
25    type Span = Range<usize>;
26    type Label = ();
27
28    fn expected_input_found<Iter: IntoIterator<Item = Option<char>>>(
29        span: Self::Span,
30        expected_chars: Iter,
31        received_char: Option<char>,
32    ) -> Self {
33        Self::ExpectedFound {
34            span,
35            expected_chars: expected_chars.into_iter().collect(),
36            received_char,
37        }
38    }
39
40    fn with_label(self, _: Self::Label) -> Self {
41        self
42    }
43
44    fn merge(mut self, mut other: Self) -> Self {
45        #[allow(irrefutable_let_patterns)]
46        if let (
47            Self::ExpectedFound { expected_chars, .. },
48            Self::ExpectedFound {
49                expected_chars: expected_other,
50                ..
51            },
52        ) = (&mut self, &mut other)
53        {
54            expected_chars.append(expected_other)
55        }
56
57        self
58    }
59}
60
61fn comment() -> impl Parser<char, (), Error = ParseError> + Clone {
62    let single_line = just("//").then(take_until(text::newline())).ignored();
63
64    let multi_line = just("/*").then(take_until(just("*/"))).ignored();
65
66    single_line.or(multi_line)
67}
68
69fn optional_comment() -> impl Parser<char, (), Error = ParseError> + Clone {
70    comment().or(empty())
71}
72
73fn string() -> impl Parser<char, String, Error = ParseError> + Clone {
74    let string_char = none_of('"').or(just('\\').ignore_then(any()));
75    just('"')
76        .ignore_then(string_char.repeated())
77        .then_ignore(just('"'))
78        .padded_by(optional_comment())
79        .map(|chars| chars.into_iter().collect())
80}
81
82pub(crate) fn expression() -> impl Parser<char, Span<Expr>, Error = ParseError> + Clone {
83    let ident = text::ident().padded().padded_by(optional_comment());
84    recursive(|expr| {
85        let int = text::int(10)
86            .map_with_span(|s: String, span| {
87                Span(Expr::Value(Value::I32(s.parse().unwrap())), span)
88            })
89            .padded();
90
91        let float = text::int(10)
92            .then_ignore(just("."))
93            .then(text::int(10))
94            .map_with_span(|(l, r), span| {
95                Span(
96                    Expr::Value(Value::F32(format!("{}.{}", l, r).parse().unwrap())),
97                    span,
98                )
99            });
100
101        let bool = keyword("true")
102            .padded_by(optional_comment())
103            .to(true)
104            .or(keyword("false").padded_by(optional_comment()).to(false))
105            .map_with_span(|b, span| Span(Expr::Value(Value::Bool(b)), span));
106
107        let string = string().map_with_span(|s, span| Span(Expr::Value(Value::String(s)), span));
108
109        let named_fields = ident
110            .clone()
111            .map_with_span(Span)
112            .then_ignore(just(':'))
113            .then(expr.clone())
114            .separated_by(just(','))
115            .allow_trailing()
116            .delimited_by(just('{'), just('}'))
117            .map(|fields| ExprFields::Named(fields.into_iter().collect()));
118
119        let tuple_fields = expr
120            .clone()
121            .separated_by(just(','))
122            .allow_trailing()
123            .delimited_by(just('('), just(')'))
124            .map(ExprFields::Tuple);
125
126        let empty_fields = empty().to(ExprFields::Empty);
127
128        let enum_literal = ident
129            .clone()
130            .map_with_span(Span)
131            .then_ignore(just("::"))
132            .then(ident.clone().map_with_span(Span))
133            .then(
134                named_fields
135                    .clone()
136                    .or(tuple_fields.clone())
137                    .or(empty_fields),
138            )
139            .map_with_span(|((enum_name, variant_name), fields), span| {
140                Span(
141                    Expr::Enum {
142                        enum_name,
143                        variant_name,
144                        fields,
145                    },
146                    span,
147                )
148            });
149
150        let struct_literal = ident
151            .clone()
152            .map_with_span(Span)
153            .then(named_fields)
154            .map_with_span(|(name, fields), span| Span(Expr::Struct(name, fields), span));
155
156        let atom = float
157            .or(int)
158            .or(expr.clone().delimited_by(just('('), just(')')))
159            .or(bool)
160            .or(string)
161            .or(enum_literal)
162            .or(struct_literal)
163            .or(ident
164                .clone()
165                .map_with_span(|i, span| Span(Expr::Variable(i), span)))
166            .padded();
167
168        let args = expr
169            .clone()
170            .separated_by(just(','))
171            .allow_trailing()
172            .delimited_by(just('('), just(')'))
173            .map_with_span(|args, span| Span(PostFix::Args(args), span));
174
175        let field = just('.')
176            .ignore_then(ident.map_with_span(Span))
177            .map_with_span(|field, span| Span(PostFix::Field(field), span));
178
179        let index = just('[')
180            .ignore_then(expr.clone())
181            .then_ignore(just(']'))
182            .map_with_span(|index, span| Span(PostFix::Index(Box::new(index)), span));
183
184        let call = atom
185            .clone()
186            .then(args.or(field).or(index).repeated())
187            .foldl(|callee, post_fix| {
188                let span = (callee.1.start())..(post_fix.1.end());
189                Span(Expr::PostFix(Box::new(callee), post_fix), span)
190            });
191
192        let op = |c| just(c).padded();
193        let op2 = |c| just(c).padded();
194
195        let unary = op('-')
196            .to(UnaryOp::Negate)
197            .or(op('!').to(UnaryOp::Not))
198            .map_with_span(Span)
199            .repeated()
200            .then(call.or(atom))
201            .foldr(|op, rhs| {
202                let span = (op.1.start())..(rhs.1.end());
203                Span(Expr::Unary(op, Box::new(rhs)), span)
204            });
205
206        let product = unary
207            .clone()
208            .then(
209                op('*')
210                    .map_with_span(|_, span| Span(BinaryOp::Multiply, span))
211                    .or(op('/').map_with_span(|_, span| Span(BinaryOp::Divide, span)))
212                    .then(unary)
213                    .repeated(),
214            )
215            .foldl(|lhs, (op, rhs)| {
216                let span = (lhs.1.start())..(rhs.1.end());
217                Span(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), span)
218            });
219
220        let addition = product
221            .clone()
222            .then(
223                op('+')
224                    .to(BinaryOp::Add)
225                    .or(op('-').to(BinaryOp::Subtract))
226                    .map_with_span(Span)
227                    .then(product)
228                    .repeated(),
229            )
230            .foldl(|lhs, (op, rhs)| {
231                let span = (lhs.1.start())..(rhs.1.end());
232                Span(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), span)
233            });
234
235        addition
236            .clone()
237            .then(
238                op('>')
239                    .to(BinaryOp::GreaterThan)
240                    .or(op('<').to(BinaryOp::LessThan))
241                    .or(op2(">=").to(BinaryOp::GreaterThanOrEqual))
242                    .or(op2("<=").to(BinaryOp::LessThanOrEqual))
243                    .or(op2("==").to(BinaryOp::Equal))
244                    .or(op2("!=").to(BinaryOp::NotEqual))
245                    .map_with_span(Span)
246                    .then(addition)
247                    .repeated(),
248            )
249            .foldl(|lhs, (op, rhs)| {
250                let span = (lhs.1.start())..(rhs.1.end());
251                Span(Expr::Binary(op, Box::new(lhs), Box::new(rhs)), span)
252            })
253    })
254}
255
256fn type_signature() -> impl Parser<char, Span<TypeSig>, Error = ParseError> + Clone {
257    recursive(|type_sig| {
258        just("i32")
259            .padded()
260            .to(TypeSig::I32)
261            .or(just("f32").padded().to(TypeSig::F32))
262            .or(just("string").padded().to(TypeSig::String))
263            .or(just("bool").padded().to(TypeSig::Bool))
264            .or(ident()
265                .then(
266                    type_sig
267                        .separated_by(just(','))
268                        .delimited_by(just('<'), just('>')),
269                )
270                .map(|(name, args)| TypeSig::Named(name, args)))
271            .or(ident().map(|name| TypeSig::Named(name, vec![])))
272            .map_with_span(Span)
273    })
274}
275
276fn ident() -> impl Parser<char, Span<String>, Error = ParseError> + Clone {
277    text::ident()
278        .padded()
279        .padded_by(optional_comment())
280        .map_with_span(Span)
281}
282
283pub fn just_padded<C: OrderedContainer<char> + Clone>(
284    inputs: C,
285) -> impl Parser<char, C, Error = ParseError> + Clone {
286    just(inputs).padded_by(optional_comment())
287}
288
289pub fn keyword(s: &'static str) -> impl Parser<char, (), Error = ParseError> + Clone {
290    text::keyword(s).padded_by(optional_comment())
291}
292
293fn type_declaration() -> impl Parser<char, TypeDeclaration, Error = ParseError> {
294    let ident = ident();
295
296    let field = ident
297        .clone()
298        .then_ignore(just(':'))
299        .then(type_signature())
300        .padded();
301
302    let named_fields = field
303        .separated_by(just_padded(','))
304        .allow_trailing()
305        .padded()
306        .delimited_by(just_padded('{'), just_padded('}'))
307        .map(|fields| TypeFields::Named(fields.into_iter().collect()));
308
309    let tuple_fields = type_signature()
310        .separated_by(just_padded(','))
311        .allow_trailing()
312        .padded()
313        .delimited_by(just_padded('('), just(')'))
314        .map(TypeFields::Tuple);
315
316    let empty_fields = empty().to(TypeFields::Empty);
317
318    let type_parameters = ident
319        .clone()
320        .separated_by(just_padded(','))
321        .allow_trailing()
322        .delimited_by(just_padded('<'), just_padded('>'))
323        .map_with_span(Span)
324        .map(Some)
325        .or(empty().to(None))
326        .padded();
327
328    let struct_declaration = keyword("struct")
329        .ignore_then(ident.clone())
330        .then(type_parameters.clone())
331        .then(
332            named_fields
333                .clone()
334                .or(tuple_fields.clone())
335                .or(empty_fields.clone()),
336        )
337        .map(
338            |((name, type_parameters), fields)| TypeDeclaration::Struct {
339                name,
340                type_parameters,
341                fields,
342            },
343        );
344
345    let enum_variant = ident
346        .clone()
347        .then(named_fields.or(tuple_fields).or(empty_fields))
348        .padded();
349
350    let enum_declaration = keyword("enum")
351        .ignore_then(ident)
352        .then(type_parameters)
353        .then(
354            enum_variant
355                .separated_by(just_padded(','))
356                .allow_trailing()
357                .delimited_by(just_padded('{'), just_padded('}')),
358        )
359        .map(
360            |((name, type_parameters), variants)| TypeDeclaration::Enum {
361                name,
362                type_parameters,
363                variants: variants.into_iter().collect(),
364            },
365        );
366
367    enum_declaration.or(struct_declaration)
368}
369
370fn statement() -> impl Parser<char, Span<Stmt>, Error = ParseError> {
371    recursive(|stmt| {
372        let ident = ident();
373
374        let function_parameters = ident
375            .clone()
376            .then_ignore(just_padded(':'))
377            .then(type_signature())
378            .padded()
379            .separated_by(just_padded(','))
380            .delimited_by(just_padded('('), just_padded(')'));
381
382        let return_type = just_padded("->")
383            .padded()
384            .ignore_then(type_signature())
385            .map(Some)
386            .padded();
387
388        let optional_return_type = return_type.or(empty().to(None));
389        let mut if_expression = Recursive::declare();
390        let mut match_expression = Recursive::declare();
391        let mut expression_block = Recursive::declare();
392
393        if_expression.define(
394            keyword("if")
395                .padded()
396                .ignore_then(expression().map(Box::new))
397                .then(expression_block.clone().map_with_span(Span))
398                .then_ignore(keyword("else"))
399                .then(expression_block.clone().map_with_span(Span))
400                .map_with_span(|((condition, then_block), else_block), span| {
401                    Span(
402                        Expr::If {
403                            condition,
404                            then_block,
405                            else_block,
406                        },
407                        span,
408                    )
409                }),
410        );
411
412        let named_bindings = ident
413            .clone()
414            .then(
415                empty()
416                    .to(None)
417                    .or(just_padded(':').ignore_then(ident.clone()).map(Some)),
418            )
419            .separated_by(just_padded(','))
420            .allow_trailing()
421            .delimited_by(just_padded('{'), just_padded('}'))
422            .map(|bindings| MatchBindings::Named(bindings.into_iter().collect::<Vec<_>>()));
423
424        let tuple_bindings = ident
425            .clone()
426            .separated_by(just_padded(','))
427            .allow_trailing()
428            .delimited_by(just_padded('('), just_padded(')'))
429            .map(|bindings| MatchBindings::Tuple(bindings.into_iter().collect::<Vec<_>>()));
430
431        let match_pattern = ident
432            .clone()
433            .then_ignore(just_padded("::"))
434            .then(ident.clone())
435            .then(
436                named_bindings
437                    .or(tuple_bindings)
438                    .map(Some)
439                    .or(empty().to(None)),
440            )
441            .padded()
442            .map_with_span(|((enum_name, variant_name), fields), span| {
443                Span(
444                    MatchCase {
445                        enum_name,
446                        variant_name,
447                        fields,
448                    },
449                    span,
450                )
451            });
452
453        match_expression.define(
454            keyword("match")
455                .padded()
456                .ignore_then(expression().map(Box::new))
457                .then(
458                    match_pattern
459                        .then_ignore(just_padded("=>"))
460                        .then(expression_block.clone().map_with_span(Span))
461                        .padded()
462                        .separated_by(just_padded(','))
463                        .allow_trailing()
464                        .padded()
465                        .delimited_by(just_padded('{'), just_padded('}')),
466                )
467                .map_with_span(|(expr, cases), span| Span(Expr::Match { expr, cases }, span)),
468        );
469
470        expression_block.define(
471            stmt.clone()
472                .repeated()
473                .then(
474                    if_expression
475                        .clone()
476                        .or(match_expression.clone())
477                        .or(expression())
478                        .padded()
479                        .map(Box::new)
480                        .map(Some)
481                        .or(empty().to(None)),
482                )
483                .delimited_by(just_padded('{'), just_padded('}'))
484                .map(|(stmts, end_expr)| ExprBlock { stmts, end_expr })
485                .padded(),
486        );
487
488        let type_parameters = ident
489            .clone()
490            .separated_by(just(','))
491            .delimited_by(just('<'), just('>'))
492            .map_with_span(Span)
493            .map(Some)
494            .or(empty().to(None));
495
496        let function_decl = keyword("fn")
497            .ignore_then(ident.clone())
498            .then(type_parameters)
499            .then(function_parameters)
500            .then(optional_return_type)
501            .then(expression_block.map_with_span(Span))
502            .map_with_span(
503                |((((name, type_parameters), params), return_type), body), span| {
504                    Span(
505                        Stmt::Function(Function {
506                            name,
507                            type_parameters,
508                            params,
509                            return_type,
510                            body,
511                        }),
512                        span,
513                    )
514                },
515            );
516
517        let let_decl = keyword("let")
518            .ignore_then(ident.clone())
519            .then_ignore(just_padded('='))
520            .then(
521                if_expression
522                    .or(match_expression)
523                    .or(expression().padded().then_ignore(just_padded(';'))),
524            )
525            .map_with_span(|(ident, expr), span| Span(Stmt::Let(ident, expr), span));
526
527        let block = stmt
528            .repeated()
529            .delimited_by(just_padded('{'), just_padded('}'));
530
531        let optional_else = keyword("else")
532            .padded()
533            .ignore_then(block.clone())
534            .or(empty().to(Vec::new()));
535
536        let if_stmt = keyword("if")
537            .ignore_then(expression())
538            .then(block.clone())
539            .then(optional_else)
540            .map_with_span(|((condition, then_block), else_block), span| {
541                Span(
542                    Stmt::If {
543                        condition,
544                        then_block,
545                        else_block,
546                    },
547                    span,
548                )
549            });
550
551        let for_stmt = keyword("for")
552            .ignore_then(ident.clone())
553            .then_ignore(keyword("in"))
554            .then(expression())
555            .then(block.clone())
556            .map_with_span(|((iterator_variable, iterator), body), span| {
557                Span(
558                    Stmt::For {
559                        iterator_variable,
560                        iterator,
561                        body,
562                    },
563                    span,
564                )
565            });
566
567        let use_stmt = keyword("use")
568            .ignore_then(ident.clone())
569            .then_ignore(just_padded("::"))
570            .then(
571                ident
572                    .clone()
573                    .or(just_padded("*").map_with_span(|s, span| Span(s.to_string(), span))),
574            )
575            .then_ignore(just_padded(';'))
576            .map_with_span(|(module, name), span| Span(Stmt::Use { module, name }, span));
577
578        let import_stmt = keyword("import")
579            .padded()
580            .ignore_then(
581                keyword("extern")
582                    .padded()
583                    .to(ImportType::External)
584                    .or(empty().to(ImportType::Internal))
585                    .map_with_span(Span),
586            )
587            .then(ident.clone().map(Some).or(empty().to(None)))
588            .then_ignore(just_padded(',').padded().to(()).or(empty()))
589            .then(
590                ident
591                    .separated_by(just_padded(','))
592                    .allow_trailing()
593                    .delimited_by(just_padded('{'), just_padded('}'))
594                    .or(empty().to(Vec::new())),
595            )
596            .then_ignore(keyword("from").padded())
597            .then(string().map_with_span(Span))
598            .then_ignore(just_padded(';'))
599            .map_with_span(
600                |(((import_type, default_import), named_imports), module), span| {
601                    Span(
602                        Stmt::Import {
603                            ty: import_type,
604                            default_import,
605                            named_imports,
606                            path: module,
607                        },
608                        span,
609                    )
610                },
611            );
612
613        let type_declaration =
614            type_declaration().map_with_span(|decl, span| Span(Stmt::Type(decl), span));
615
616        let return_stmt = keyword("return")
617            .ignore_then(expression().padded().map(Some).or(empty().to(None)))
618            .then_ignore(just_padded(';'))
619            .map_with_span(|expr, span| Span(Stmt::Return(expr), span));
620
621        function_decl
622            .or(let_decl)
623            .or(if_stmt)
624            .or(for_stmt)
625            .or(return_stmt)
626            .or(import_stmt)
627            .or(type_declaration)
628            .or(use_stmt)
629            .or(expression()
630                .then_ignore(just_padded(';'))
631                .map_with_span(|expr, span| Span(Stmt::Expr(expr), span)))
632            .padded()
633            .padded_by(optional_comment())
634    })
635}
636
637fn parser() -> impl Parser<char, Vec<Span<Stmt>>, Error = ParseError> {
638    comment()
639        .to(None)
640        .or(statement().map(Some))
641        .repeated()
642        .flatten()
643        .then_ignore(end())
644}
645
646pub fn parse(source: &str) -> (Option<Program>, Vec<ParseError>) {
647    let (output, errors) = parser().parse_recovery(source);
648    let program = output.map(|statements| Program { statements });
649
650    (program, errors)
651}
652
653#[cfg(test)]
654mod tests {
655    use super::*;
656
657    #[test]
658    fn test_parse_bool() {
659        insta::assert_yaml_snapshot!(expression().parse("true"));
660        insta::assert_yaml_snapshot!(expression().parse("false"));
661    }
662
663    #[test]
664    fn test_parse_int() {
665        insta::assert_yaml_snapshot!(expression().parse("10"));
666        insta::assert_yaml_snapshot!(expression().parse("2"));
667    }
668
669    #[test]
670    fn test_parse_float() {
671        insta::assert_yaml_snapshot!(expression().parse("10.5"));
672        insta::assert_yaml_snapshot!(expression().parse("2.1"));
673    }
674
675    #[test]
676    fn test_parse_variable() {
677        insta::assert_yaml_snapshot!(expression().parse("abcd"));
678        insta::assert_yaml_snapshot!(expression().parse("foo_bar"));
679        insta::assert_yaml_snapshot!(expression().parse("fooBar"));
680        insta::assert_yaml_snapshot!(expression().parse("_fooBar"));
681        insta::assert_yaml_snapshot!(expression().parse("_"));
682        insta::assert_yaml_snapshot!(expression().parse("a3"));
683    }
684
685    #[test]
686    fn test_parse_struct_literal() {
687        insta::assert_yaml_snapshot!(expression().parse("Foo { a: 10, b: 20 }"));
688        insta::assert_yaml_snapshot!(expression().parse("Foo { a: 10 }"));
689
690        insta::assert_yaml_snapshot!(expression().parse("Foo::Bar { a: 10, b: 20 }"));
691    }
692
693    #[test]
694    fn test_parse_unary() {
695        insta::assert_yaml_snapshot!(expression().parse("-10"));
696
697        insta::assert_yaml_snapshot!(expression().parse("!bar"));
698
699        insta::assert_yaml_snapshot!(expression().parse("-!10"));
700    }
701
702    #[test]
703    fn test_parse_multiply() {
704        insta::assert_yaml_snapshot!(expression().parse("10 * 11"));
705
706        insta::assert_yaml_snapshot!(expression().parse("10 / 11"));
707
708        insta::assert_yaml_snapshot!(expression().parse("10 / 11 / 12"));
709    }
710
711    #[test]
712    fn test_parse_addition() {
713        insta::assert_yaml_snapshot!(expression().parse("10 + 11"));
714
715        insta::assert_yaml_snapshot!(expression().parse("10 - 11"));
716    }
717
718    #[test]
719    fn test_parse_parens() {
720        insta::assert_yaml_snapshot!(expression().parse("foo + (11 * 2)"));
721
722        insta::assert_yaml_snapshot!(expression().parse("(10 - 11)"));
723    }
724
725    #[test]
726    fn test_parse_call() {
727        insta::assert_yaml_snapshot!(expression().parse("foo()"));
728
729        insta::assert_yaml_snapshot!(expression().parse("foo(10)"));
730
731        insta::assert_yaml_snapshot!(expression().parse("foo(10)(20)"));
732    }
733
734    #[test]
735    fn test_parse_function() {
736        insta::assert_yaml_snapshot!(statement().parse("fn foo() { 20 }"));
737
738        insta::assert_yaml_snapshot!(statement().parse("fn foo() -> i32 { 20 }"));
739
740        insta::assert_yaml_snapshot!(statement().parse("fn foo() { let name = 10; }"));
741    }
742
743    #[test]
744    fn test_parse_statement() {
745        insta::assert_yaml_snapshot!(statement().parse("let a = 10;"));
746
747        insta::assert_yaml_snapshot!(statement().parse("let a = if b { 10 } else { 20 }"));
748
749        insta::assert_yaml_snapshot!(statement().repeated().parse(
750            "let a = if b { 10 } else { 20 }
751            10 + 11;
752            let h = foobar;"
753        ))
754    }
755
756    #[test]
757    fn test_parse_if_statement() {
758        insta::assert_yaml_snapshot!(statement().parse(
759            "if b {
760               let a = 10;
761             } else {
762               let b = 20;
763             }"
764        ));
765
766        insta::assert_yaml_snapshot!(statement().parse("if b { 10; }"));
767    }
768
769    #[test]
770    fn test_parse_import_statement() {
771        insta::assert_yaml_snapshot!(statement().parse(r#"import foo from "./bar";"#));
772
773        insta::assert_yaml_snapshot!(statement().parse(r#"import { foo, bar } from "./baz";"#));
774
775        insta::assert_yaml_snapshot!(statement().parse(r#"import qux, { foo, bar } from "./baz";"#));
776
777        insta::assert_yaml_snapshot!(
778            statement().parse(r#"import extern { foo, bar } from "https://example.com/baz";"#)
779        );
780    }
781
782    #[test]
783    fn test_parse_return_statement() {
784        insta::assert_yaml_snapshot!(statement().parse("return 10;"));
785
786        insta::assert_yaml_snapshot!(statement().parse("return;"));
787    }
788
789    #[test]
790    fn test_parse_struct_declaration() {
791        insta::assert_yaml_snapshot!(type_declaration().parse("struct Foo { a: i32, b: i32 }"));
792
793        insta::assert_yaml_snapshot!(type_declaration().parse("struct Foo {}"));
794
795        insta::assert_yaml_snapshot!(type_declaration().parse("struct Foo { a: i32 }"));
796
797        insta::assert_yaml_snapshot!(type_declaration().parse("struct Foo { a: i32, }"));
798    }
799
800    #[test]
801    fn test_parse_enum_declaration() {
802        insta::assert_yaml_snapshot!(type_declaration().parse("enum Foo { A, B, C }"));
803
804        insta::assert_yaml_snapshot!(type_declaration().parse("enum Foo { A }"));
805
806        insta::assert_yaml_snapshot!(type_declaration().parse("enum Foo { A { foo: string } }"));
807    }
808
809    #[test]
810    fn test_parse_use_statement() {
811        insta::assert_yaml_snapshot!(statement().parse("use Foo::Bar;"));
812
813        insta::assert_yaml_snapshot!(statement().parse("use Foo::*;"));
814    }
815}