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}