Fork of daniellemaywood.uk/gleam — Wasm codegen work
2

Configure Feed

Select the types of activity you want to include in your feed.

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}