Fork of daniellemaywood.uk/gleam — Wasm codegen work
28 kB
890 lines
1use std::ops::Deref;
2
3use ecow::{EcoString, eco_format};
4use im::HashSet;
5use num_bigint::BigInt;
6use vec1::Vec1;
7
8use crate::{ast, exhaustiveness, parse, type_};
9
10#[derive(Debug, PartialEq, Eq, Default)]
11pub struct Module {
12 pub functions: Vec<Function>,
13}
14
15#[derive(Debug, PartialEq, Eq)]
16pub struct Function {
17 pub name: EcoString,
18 pub return_type: Type,
19 pub parameters: Vec<FunctionParameter>,
20 pub body: Expression,
21}
22
23#[derive(Debug, PartialEq, Eq)]
24pub struct FunctionParameter {
25 pub type_: Type,
26 pub name: Option<EcoString>,
27}
28
29#[derive(Debug, Default, PartialEq, Eq)]
30pub struct Translator {
31 module: Module,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
35pub enum Type {
36 Int,
37 Float,
38 Bool,
39 Generic,
40}
41
42#[derive(Debug, PartialEq, Eq, Clone)]
43pub enum Expression {
44 Block(Vec<Expression>),
45
46 Var {
47 name: EcoString,
48 },
49
50 Int {
51 value: BigInt,
52 },
53
54 Equals {
55 lhs: Box<Expression>,
56 rhs: Box<Expression>,
57 },
58
59 IntAdd {
60 lhs: Box<Expression>,
61 rhs: Box<Expression>,
62 },
63
64 Set {
65 name: EcoString,
66 value: Box<Expression>,
67 },
68
69 If {
70 cond: Box<Expression>,
71 then: Box<Expression>,
72 else_: Box<Expression>,
73 },
74}
75
76impl Translator {
77 pub fn translate(mut self, module: &ast::TypedModule) -> Module {
78 for definition in &module.definitions {
79 match definition {
80 ast::Definition::Function(function) => {
81 self.module
82 .functions
83 .push(Self::translate_function(function));
84 }
85 ast::Definition::TypeAlias(_type_alias) => todo!(),
86 ast::Definition::CustomType(_custom_type) => todo!(),
87 ast::Definition::Import(_import) => todo!(),
88 ast::Definition::ModuleConstant(_module_constant) => todo!(),
89 }
90 }
91
92 self.module
93 }
94
95 fn translate_function(function: &ast::TypedFunction) -> Function {
96 let (_, name) = function.name.clone().expect("function should have a name");
97
98 let return_type = Self::translate_type(&function.return_type);
99
100 let parameters = Iterator::collect(function.arguments.iter().map(|argument| {
101 let name = argument.get_variable_name().cloned();
102 let type_ = Self::translate_type(&argument.type_);
103
104 FunctionParameter { name, type_ }
105 }));
106
107 let body = Self::flatten_expression(BodyTranslator::default().translate(&function.body));
108
109 Function {
110 name,
111 body,
112 return_type,
113 parameters,
114 }
115 }
116
117 fn translate_type(type_: &type_::Type) -> Type {
118 match type_ {
119 type_::Type::Named {
120 publicity: _,
121 package: _,
122 module,
123 name,
124 arguments: _,
125 inferred_variant: _,
126 } => match (module.as_str(), name.as_str()) {
127 ("gleam", "Int") => Type::Int,
128 ("gleam", "Float") => Type::Float,
129 ("gleam", "Bool") => Type::Bool,
130 (_, _) => todo!(),
131 },
132 type_::Type::Fn {
133 arguments: _,
134 return_: _,
135 } => todo!(),
136 type_::Type::Var { type_ } => match type_.borrow().deref() {
137 type_::TypeVar::Link { type_ } => Self::translate_type(type_),
138 type_::TypeVar::Unbound { id: _ } | type_::TypeVar::Generic { id: _ } => {
139 Type::Generic
140 }
141 },
142 type_::Type::Tuple { elements: _ } => todo!(),
143 }
144 }
145
146 fn flatten_expression(expression: Expression) -> Expression {
147 match expression {
148 Expression::Block(expressions) => {
149 let mut block = vec![];
150
151 for expression in expressions {
152 let expression = Self::flatten_expression(expression);
153
154 match expression {
155 Expression::Block(mut expressions) => block.append(&mut expressions),
156 Expression::Var { .. }
157 | Expression::Int { .. }
158 | Expression::Equals { .. }
159 | Expression::IntAdd { .. }
160 | Expression::Set { .. }
161 | Expression::If { .. } => block.push(expression),
162 };
163 }
164
165 match block.as_slice() {
166 [single] => single.clone(),
167 _ => Expression::Block(block),
168 }
169 }
170 Expression::Var { name: _ } => expression,
171 Expression::Int { value: _ } => expression,
172 Expression::Equals { lhs, rhs } => Expression::Equals {
173 lhs: Box::new(Self::flatten_expression(*lhs)),
174 rhs: Box::new(Self::flatten_expression(*rhs)),
175 },
176 Expression::IntAdd { lhs, rhs } => Expression::IntAdd {
177 lhs: Box::new(Self::flatten_expression(*lhs)),
178 rhs: Box::new(Self::flatten_expression(*rhs)),
179 },
180 Expression::Set { name, value } => Expression::Set {
181 name,
182 value: Box::new(Self::flatten_expression(*value)),
183 },
184 Expression::If { cond, then, else_ } => Expression::If {
185 cond: Box::new(Self::flatten_expression(*cond)),
186 then: Box::new(Self::flatten_expression(*then)),
187 else_: Box::new(Self::flatten_expression(*else_)),
188 },
189 }
190 }
191}
192
193#[derive(Debug, Default)]
194pub struct BodyTranslator {
195 variables: HashSet<EcoString>,
196 variable_count: usize,
197}
198
199#[derive(Debug, Clone, Copy, PartialEq, Eq)]
200enum DecisionKind {
201 Case,
202 Let,
203}
204
205impl BodyTranslator {
206 fn translate(mut self, body: &Vec1<ast::TypedStatement>) -> Expression {
207 Expression::Block(Iterator::collect(
208 body.iter().map(|stmt| self.translate_statement(stmt)),
209 ))
210 }
211
212 fn translate_statement(&mut self, statement: &ast::TypedStatement) -> Expression {
213 match statement {
214 ast::Statement::Expression(expression) => self.translate_expression(expression),
215 ast::Statement::Assignment(assignment) => self.translate_assignment(assignment),
216 ast::Statement::Use(_) => todo!(),
217 ast::Statement::Assert(_) => todo!(),
218 }
219 }
220
221 fn translate_expression(&mut self, expression: &ast::TypedExpr) -> Expression {
222 match expression {
223 ast::TypedExpr::Int {
224 location: _,
225 type_: _,
226 value: _,
227 int_value,
228 } => Expression::Int {
229 value: int_value.clone(),
230 },
231 ast::TypedExpr::Float {
232 location: _,
233 type_: _,
234 value: _,
235 } => todo!(),
236 ast::TypedExpr::String {
237 location: _,
238 type_: _,
239 value: _,
240 } => todo!(),
241 ast::TypedExpr::Block {
242 location: _,
243 statements: _,
244 } => todo!(),
245 ast::TypedExpr::Pipeline {
246 location: _,
247 first_value: _,
248 assignments: _,
249 finally: _,
250 finally_kind: _,
251 } => todo!(),
252 ast::TypedExpr::Var {
253 location: _,
254 constructor: _,
255 name,
256 } => Expression::Var { name: name.clone() },
257 ast::TypedExpr::Fn {
258 location: _,
259 type_: _,
260 kind: _,
261 arguments: _,
262 body: _,
263 return_annotation: _,
264 purity: _,
265 } => todo!(),
266 ast::TypedExpr::List {
267 location: _,
268 type_: _,
269 elements: _,
270 tail: _,
271 } => todo!(),
272 ast::TypedExpr::Call {
273 location: _,
274 type_: _,
275 fun: _,
276 arguments: _,
277 } => todo!(),
278 ast::TypedExpr::BinOp {
279 location: _,
280 type_: _,
281 name,
282 name_location: _,
283 left,
284 right,
285 } => {
286 let lhs = Box::new(self.translate_expression(left));
287 let rhs = Box::new(self.translate_expression(right));
288
289 match name {
290 ast::BinOp::And => todo!(),
291 ast::BinOp::Or => todo!(),
292 ast::BinOp::Eq => Expression::Equals { lhs, rhs },
293 ast::BinOp::NotEq => todo!(),
294 ast::BinOp::LtInt => todo!(),
295 ast::BinOp::LtEqInt => todo!(),
296 ast::BinOp::LtFloat => todo!(),
297 ast::BinOp::LtEqFloat => todo!(),
298 ast::BinOp::GtEqInt => todo!(),
299 ast::BinOp::GtInt => todo!(),
300 ast::BinOp::GtEqFloat => todo!(),
301 ast::BinOp::GtFloat => todo!(),
302 ast::BinOp::AddInt => Expression::IntAdd { lhs, rhs },
303 ast::BinOp::AddFloat => todo!(),
304 ast::BinOp::SubInt => todo!(),
305 ast::BinOp::SubFloat => todo!(),
306 ast::BinOp::MultInt => todo!(),
307 ast::BinOp::MultFloat => todo!(),
308 ast::BinOp::DivInt => todo!(),
309 ast::BinOp::DivFloat => todo!(),
310 ast::BinOp::RemainderInt => todo!(),
311 ast::BinOp::Concatenate => todo!(),
312 }
313 }
314 ast::TypedExpr::Case {
315 location: _,
316 type_: _,
317 subjects,
318 clauses,
319 compiled_case,
320 } => {
321 let mut block = vec![];
322
323 let subject_vars: Vec<_> = Iterator::collect(subjects.iter().map(|subject| {
324 let name = self.make_tmp_var();
325 let value = self.translate_expression(subject);
326
327 block.push(Expression::Set {
328 name: name.clone(),
329 value: Box::new(value),
330 });
331
332 return name;
333 }));
334
335 block.push(self.translate_decision(
336 DecisionKind::Case,
337 &subject_vars,
338 clauses,
339 &compiled_case.tree,
340 ));
341
342 Expression::Block(block)
343 }
344 ast::TypedExpr::RecordAccess {
345 location: _,
346 field_start: _,
347 type_: _,
348 label: _,
349 index: _,
350 record: _,
351 } => todo!(),
352 ast::TypedExpr::ModuleSelect {
353 location: _,
354 field_start: _,
355 type_: _,
356 label: _,
357 module_name: _,
358 module_alias: _,
359 constructor: _,
360 } => todo!(),
361 ast::TypedExpr::Tuple {
362 location: _,
363 type_: _,
364 elements: _,
365 } => todo!(),
366 ast::TypedExpr::TupleIndex {
367 location: _,
368 type_: _,
369 index: _,
370 tuple: _,
371 } => todo!(),
372 ast::TypedExpr::Todo {
373 location: _,
374 message: _,
375 kind: _,
376 type_: _,
377 } => todo!(),
378 ast::TypedExpr::Panic {
379 location: _,
380 message: _,
381 type_: _,
382 } => todo!(),
383 ast::TypedExpr::Echo {
384 location: _,
385 type_: _,
386 expression: _,
387 message: _,
388 } => todo!(),
389 ast::TypedExpr::BitArray {
390 location: _,
391 type_: _,
392 segments: _,
393 } => todo!(),
394 ast::TypedExpr::RecordUpdate {
395 location: _,
396 type_: _,
397 record_assignment: _,
398 constructor: _,
399 arguments: _,
400 } => todo!(),
401 ast::TypedExpr::NegateBool {
402 location: _,
403 value: _,
404 } => todo!(),
405 ast::TypedExpr::NegateInt {
406 location: _,
407 value: _,
408 } => todo!(),
409 ast::TypedExpr::Invalid {
410 location: _,
411 type_: _,
412 } => todo!(),
413 }
414 }
415
416 fn translate_assignment(&mut self, assignment: &ast::TypedAssignment) -> Expression {
417 let mut block = vec![];
418
419 let value_var = self.make_tmp_var();
420 let value = self.translate_expression(&assignment.value);
421
422 block.push(Expression::Set {
423 name: value_var.clone(),
424 value: Box::new(value),
425 });
426
427 block.push(self.translate_decision(
428 DecisionKind::Let,
429 &[value_var.clone()],
430 &[],
431 &assignment.compiled_case.tree,
432 ));
433
434 block.push(Expression::Var { name: value_var });
435
436 Expression::Block(block)
437 }
438
439 fn make_tmp_var(&mut self) -> EcoString {
440 let variable_count = self.variable_count;
441 self.make_var(eco_format!("_tmp{variable_count}"))
442 }
443
444 fn make_var(&mut self, name: EcoString) -> EcoString {
445 self.variable_count += 1;
446 _ = self.variables.insert(name.clone());
447 name
448 }
449
450 fn translate_decision(
451 &mut self,
452 kind: DecisionKind,
453 subjects: &[EcoString],
454 clauses: &[ast::TypedClause],
455 decision: &exhaustiveness::Decision,
456 ) -> Expression {
457 match decision {
458 exhaustiveness::Decision::Run { body } => {
459 let mut block = vec![];
460
461 for (binding, value) in &body.bindings {
462 block.push(self.translate_binding(subjects, binding.clone(), value));
463 }
464
465 if kind == DecisionKind::Case {
466 block.push(self.translate_expression(&clauses[body.clause_index].then));
467 }
468
469 Expression::Block(block)
470 }
471 exhaustiveness::Decision::Guard {
472 guard,
473 if_true,
474 if_false,
475 } => {
476 let mut block = vec![];
477
478 let guard = clauses[*guard]
479 .guard
480 .as_ref()
481 .expect("expected clause to have a guard");
482
483 let referenced_in_guard = guard.referenced_variables();
484
485 let (guard_bindings, if_true_bindings): (Vec<_>, Vec<_>) = if_true
486 .bindings
487 .iter()
488 .partition(|(binding, _)| referenced_in_guard.contains(binding));
489
490 for (binding, value) in guard_bindings {
491 block.push(self.translate_binding(subjects, binding.clone(), value));
492 }
493
494 let cond = self.translate_guard(guard);
495
496 let if_true = {
497 let mut block = vec![];
498
499 for (binding, value) in if_true_bindings {
500 block.push(self.translate_binding(subjects, binding.clone(), value));
501 }
502
503 block.push(self.translate_expression(&clauses[if_true.clause_index].then));
504 block
505 };
506
507 let if_false = self.translate_decision(kind, subjects, clauses, if_false);
508
509 block.push(Expression::If {
510 cond: Box::new(cond),
511 then: Box::new(Expression::Block(if_true)),
512 else_: Box::new(if_false),
513 });
514
515 Expression::Block(block)
516 }
517 exhaustiveness::Decision::Switch {
518 var,
519 choices,
520 fallback,
521 fallback_check: _,
522 } => {
523 let fallback = self.translate_decision(kind, subjects, clauses, fallback);
524
525 DoubleEndedIterator::rfold(choices.iter(), fallback, |fallback, (check, then)| {
526 Expression::If {
527 cond: Box::new(self.translate_runtime_check(
528 check,
529 Expression::Var {
530 name: subjects[var.id].clone(),
531 },
532 )),
533 then: Box::new(self.translate_decision(kind, subjects, clauses, then)),
534 else_: Box::new(fallback),
535 }
536 })
537 }
538 exhaustiveness::Decision::Fail => todo!(),
539 }
540 }
541
542 fn translate_guard(&mut self, guard: &ast::TypedClauseGuard) -> Expression {
543 match guard {
544 ast::ClauseGuard::Block {
545 location: _,
546 value: _,
547 } => todo!(),
548 ast::ClauseGuard::Equals {
549 location: _,
550 left,
551 right,
552 } => {
553 let lhs = self.translate_guard(left);
554 let rhs = self.translate_guard(right);
555
556 Expression::Equals {
557 lhs: Box::new(lhs),
558 rhs: Box::new(rhs),
559 }
560 }
561 ast::ClauseGuard::NotEquals {
562 location: _,
563 left: _,
564 right: _,
565 } => todo!(),
566 ast::ClauseGuard::GtInt {
567 location: _,
568 left: _,
569 right: _,
570 } => todo!(),
571 ast::ClauseGuard::GtEqInt {
572 location: _,
573 left: _,
574 right: _,
575 } => todo!(),
576 ast::ClauseGuard::LtInt {
577 location: _,
578 left: _,
579 right: _,
580 } => todo!(),
581 ast::ClauseGuard::LtEqInt {
582 location: _,
583 left: _,
584 right: _,
585 } => todo!(),
586 ast::ClauseGuard::GtFloat {
587 location: _,
588 left: _,
589 right: _,
590 } => todo!(),
591 ast::ClauseGuard::GtEqFloat {
592 location: _,
593 left: _,
594 right: _,
595 } => todo!(),
596 ast::ClauseGuard::LtFloat {
597 location: _,
598 left: _,
599 right: _,
600 } => todo!(),
601 ast::ClauseGuard::LtEqFloat {
602 location: _,
603 left: _,
604 right: _,
605 } => todo!(),
606 ast::ClauseGuard::AddInt {
607 location: _,
608 left: _,
609 right: _,
610 } => todo!(),
611 ast::ClauseGuard::AddFloat {
612 location: _,
613 left: _,
614 right: _,
615 } => todo!(),
616 ast::ClauseGuard::SubInt {
617 location: _,
618 left: _,
619 right: _,
620 } => todo!(),
621 ast::ClauseGuard::SubFloat {
622 location: _,
623 left: _,
624 right: _,
625 } => todo!(),
626 ast::ClauseGuard::MultInt {
627 location: _,
628 left: _,
629 right: _,
630 } => todo!(),
631 ast::ClauseGuard::MultFloat {
632 location: _,
633 left: _,
634 right: _,
635 } => todo!(),
636 ast::ClauseGuard::DivInt {
637 location: _,
638 left: _,
639 right: _,
640 } => todo!(),
641 ast::ClauseGuard::DivFloat {
642 location: _,
643 left: _,
644 right: _,
645 } => todo!(),
646 ast::ClauseGuard::RemainderInt {
647 location: _,
648 left: _,
649 right: _,
650 } => todo!(),
651 ast::ClauseGuard::Or {
652 location: _,
653 left: _,
654 right: _,
655 } => todo!(),
656 ast::ClauseGuard::And {
657 location: _,
658 left: _,
659 right: _,
660 } => todo!(),
661 ast::ClauseGuard::Not {
662 location: _,
663 expression: _,
664 } => todo!(),
665 ast::ClauseGuard::Var {
666 location: _,
667 type_: _,
668 name,
669 definition_location: _,
670 } => Expression::Var { name: name.clone() },
671 ast::ClauseGuard::TupleIndex {
672 location: _,
673 index: _,
674 type_: _,
675 tuple: _,
676 } => todo!(),
677 ast::ClauseGuard::FieldAccess {
678 label_location: _,
679 index: _,
680 label: _,
681 type_: _,
682 container: _,
683 } => todo!(),
684 ast::ClauseGuard::ModuleSelect {
685 location: _,
686 type_: _,
687 label: _,
688 module_name: _,
689 module_alias: _,
690 literal: _,
691 } => todo!(),
692 ast::ClauseGuard::Constant(constant) => self.translate_constant(constant),
693 }
694 }
695
696 fn translate_constant(&mut self, constant: &ast::TypedConstant) -> Expression {
697 match constant {
698 ast::Constant::Int {
699 location: _,
700 value: _,
701 int_value,
702 } => Expression::Int {
703 value: int_value.clone(),
704 },
705 ast::Constant::Float {
706 location: _,
707 value: _,
708 } => todo!(),
709 ast::Constant::String {
710 location: _,
711 value: _,
712 } => todo!(),
713 ast::Constant::Tuple {
714 location: _,
715 elements: _,
716 } => todo!(),
717 ast::Constant::List {
718 location: _,
719 elements: _,
720 type_: _,
721 } => todo!(),
722 ast::Constant::Record {
723 location: _,
724 module: _,
725 name: _,
726 arguments: _,
727 tag: _,
728 type_: _,
729 field_map: _,
730 record_constructor: _,
731 } => todo!(),
732 ast::Constant::BitArray {
733 location: _,
734 segments: _,
735 } => todo!(),
736 ast::Constant::Var {
737 location: _,
738 module: _,
739 name: _,
740 constructor: _,
741 type_: _,
742 } => todo!(),
743 ast::Constant::StringConcatenation {
744 location: _,
745 left: _,
746 right: _,
747 } => todo!(),
748 ast::Constant::Invalid {
749 location: _,
750 type_: _,
751 } => todo!(),
752 }
753 }
754
755 fn translate_binding(
756 &mut self,
757 subjects: &[EcoString],
758 binding: EcoString,
759 value: &exhaustiveness::BoundValue,
760 ) -> Expression {
761 let value = match value {
762 exhaustiveness::BoundValue::Variable(variable) => Expression::Var {
763 name: subjects[variable.id].clone(),
764 },
765 exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(),
766 exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int {
767 value: big_int.clone(),
768 },
769 exhaustiveness::BoundValue::LiteralFloat(_eco_string) => todo!(),
770 exhaustiveness::BoundValue::BitArraySlice {
771 bit_array: _,
772 read_action: _,
773 } => todo!(),
774 };
775
776 Expression::Set {
777 name: binding,
778 value: Box::new(value),
779 }
780 }
781
782 fn translate_runtime_check(
783 &mut self,
784 check: &exhaustiveness::RuntimeCheck,
785 against: Expression,
786 ) -> Expression {
787 match check {
788 exhaustiveness::RuntimeCheck::Int { value } => {
789 let value = parse::parse_int_value(&value).expect("unable to parse integer value");
790
791 Expression::Equals {
792 lhs: Box::new(against),
793 rhs: Box::new(Expression::Int { value }),
794 }
795 }
796 exhaustiveness::RuntimeCheck::Float { value: _ } => todo!(),
797 exhaustiveness::RuntimeCheck::String { value: _ } => todo!(),
798 exhaustiveness::RuntimeCheck::StringPrefix { prefix: _, rest: _ } => todo!(),
799 exhaustiveness::RuntimeCheck::Tuple {
800 size: _,
801 elements: _,
802 } => todo!(),
803 exhaustiveness::RuntimeCheck::BitArray { test: _ } => todo!(),
804 exhaustiveness::RuntimeCheck::Variant {
805 match_: _,
806 index: _,
807 labels: _,
808 fields: _,
809 } => todo!(),
810 exhaustiveness::RuntimeCheck::NonEmptyList { first: _, rest: _ } => todo!(),
811 exhaustiveness::RuntimeCheck::EmptyList => todo!(),
812 }
813 }
814}
815
816#[cfg(test)]
817mod tests {
818 use super::*;
819 use crate::analyse::TargetSupport;
820 use crate::build::{Origin, Target};
821 use crate::config::PackageConfig;
822 use crate::line_numbers::LineNumbers;
823 use crate::parse::parse_module;
824 use crate::type_::PRELUDE_MODULE_NAME;
825 use crate::uid::UniqueIdGenerator;
826 use crate::warning::{TypeWarningEmitter, WarningEmitter};
827 use camino::Utf8PathBuf;
828
829 fn compile_module(src: &str) -> ast::TypedModule {
830 use crate::type_::build_prelude;
831 let parsed = parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null())
832 .expect("syntax error");
833 let ast = parsed.module;
834 let ids = UniqueIdGenerator::new();
835 let mut config = PackageConfig::default();
836 config.name = "thepackage".into();
837 let mut modules = im::HashMap::new();
838 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids));
839 let line_numbers = LineNumbers::new(src);
840
841 crate::analyse::ModuleAnalyzerConstructor::<()> {
842 target: Target::Erlang,
843 ids: &ids,
844 origin: Origin::Src,
845 importable_modules: &modules,
846 warnings: &TypeWarningEmitter::null(),
847 direct_dependencies: &std::collections::HashMap::new(),
848 dev_dependencies: &std::collections::HashSet::new(),
849 target_support: TargetSupport::Enforced,
850 package_config: &config,
851 }
852 .infer_module(ast, line_numbers, "".into())
853 .expect("should successfully infer")
854 }
855
856 fn translate(src: &str) -> Module {
857 let typed_module = compile_module(src);
858
859 Translator::default().translate(&typed_module)
860 }
861
862 #[test]
863 fn translates_samples() {
864 insta::assert_debug_snapshot!(translate(
865 r#"pub fn add(lhs: Int, rhs: Int) -> Int {
866 lhs + rhs
867 }"#
868 ));
869
870 insta::assert_debug_snapshot!(translate(
871 r#"pub fn some_case(n: Int) -> Int {
872 case n {
873 0 -> 2
874 1 -> 4
875 _ -> 6
876 }
877 }"#
878 ));
879
880 insta::assert_debug_snapshot!(translate(
881 r#"pub fn some_case(n: Int) -> Int {
882 case n {
883 y if y == 0 -> 2
884 1 -> 4
885 _ -> 6
886 }
887 }"#
888 ));
889 }
890}