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

Configure Feed

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

1use std::{collections::HashMap, ops::Deref}; 2 3use ecow::{EcoString, eco_format}; 4use num_bigint::BigInt; 5use vec1::Vec1; 6 7use crate::{ast, exhaustiveness, parse, type_}; 8 9#[derive(Debug, PartialEq, Eq, Default)] 10pub struct Module { 11 pub functions: Vec<Function>, 12} 13 14#[derive(Debug, PartialEq, Eq)] 15pub struct Function { 16 pub name: EcoString, 17 pub return_type: Type, 18 pub parameters: Vec<FunctionParameter>, 19 pub body: Expression, 20} 21 22#[derive(Debug, PartialEq, Eq)] 23pub struct FunctionParameter { 24 pub type_: Type, 25 pub name: Option<EcoString>, 26} 27 28#[derive(Debug, Default, PartialEq, Eq)] 29pub struct Translator { 30 module: Module, 31} 32 33#[derive(Debug, Clone, Copy, PartialEq, Eq)] 34pub enum Type { 35 Int, 36 Float, 37 Bool, 38 String, 39 Generic, 40} 41 42#[derive(Debug, PartialEq, Eq, Clone)] 43pub struct Var { 44 pub name: EcoString, 45} 46 47#[derive(Debug, PartialEq, Eq, Clone)] 48pub enum Expression { 49 Block(Vec<Expression>), 50 51 FunctionRef { 52 module: EcoString, 53 name: EcoString, 54 }, 55 56 Var(Var), 57 58 Int { 59 value: BigInt, 60 }, 61 62 Float { 63 value: EcoString, 64 }, 65 66 Bool { 67 value: bool, 68 }, 69 70 String { 71 value: EcoString, 72 }, 73 74 Equals { 75 lhs: Box<Expression>, 76 rhs: Box<Expression>, 77 }, 78 79 NotEquals { 80 lhs: Box<Expression>, 81 rhs: Box<Expression>, 82 }, 83 84 IntGt { 85 lhs: Box<Expression>, 86 rhs: Box<Expression>, 87 }, 88 89 IntGtEq { 90 lhs: Box<Expression>, 91 rhs: Box<Expression>, 92 }, 93 94 IntLt { 95 lhs: Box<Expression>, 96 rhs: Box<Expression>, 97 }, 98 99 IntLtEq { 100 lhs: Box<Expression>, 101 rhs: Box<Expression>, 102 }, 103 104 IntAdd { 105 lhs: Box<Expression>, 106 rhs: Box<Expression>, 107 }, 108 109 IntSub { 110 lhs: Box<Expression>, 111 rhs: Box<Expression>, 112 }, 113 114 IntMul { 115 lhs: Box<Expression>, 116 rhs: Box<Expression>, 117 }, 118 119 IntDiv { 120 lhs: Box<Expression>, 121 rhs: Box<Expression>, 122 }, 123 124 FloatGt { 125 lhs: Box<Expression>, 126 rhs: Box<Expression>, 127 }, 128 129 FloatGtEq { 130 lhs: Box<Expression>, 131 rhs: Box<Expression>, 132 }, 133 134 FloatLt { 135 lhs: Box<Expression>, 136 rhs: Box<Expression>, 137 }, 138 139 FloatLtEq { 140 lhs: Box<Expression>, 141 rhs: Box<Expression>, 142 }, 143 144 FloatAdd { 145 lhs: Box<Expression>, 146 rhs: Box<Expression>, 147 }, 148 149 FloatSub { 150 lhs: Box<Expression>, 151 rhs: Box<Expression>, 152 }, 153 154 FloatMul { 155 lhs: Box<Expression>, 156 rhs: Box<Expression>, 157 }, 158 159 FloatDiv { 160 lhs: Box<Expression>, 161 rhs: Box<Expression>, 162 }, 163 164 StringConcat { 165 lhs: Box<Expression>, 166 rhs: Box<Expression>, 167 }, 168 169 Set { 170 name: Var, 171 value: Box<Expression>, 172 }, 173 174 If { 175 cond: Box<Expression>, 176 then: Box<Expression>, 177 else_: Box<Expression>, 178 }, 179 180 Call { 181 target: Box<Expression>, 182 args: Vec<Expression>, 183 }, 184} 185 186impl Translator { 187 pub fn translate(mut self, module: &ast::TypedModule) -> Module { 188 for definition in &module.definitions { 189 match definition { 190 ast::Definition::Function(function) => { 191 self.module 192 .functions 193 .push(Self::translate_function(function)); 194 } 195 ast::Definition::TypeAlias(_type_alias) => todo!(), 196 ast::Definition::CustomType(_custom_type) => todo!(), 197 ast::Definition::Import(_import) => todo!(), 198 ast::Definition::ModuleConstant(_module_constant) => todo!(), 199 } 200 } 201 202 self.module 203 } 204 205 fn translate_function(function: &ast::TypedFunction) -> Function { 206 let (_, name) = function.name.clone().expect("function should have a name"); 207 208 let return_type = Self::translate_type(&function.return_type); 209 210 let parameters = Iterator::collect(function.arguments.iter().map(|argument| { 211 let name = argument.get_variable_name().cloned(); 212 let type_ = Self::translate_type(&argument.type_); 213 214 FunctionParameter { name, type_ } 215 })); 216 217 let mut translator = BodyTranslator::default(); 218 for argument in &function.arguments { 219 if let Some(name) = argument.get_variable_name() { 220 _ = translator.make_var(name.clone()); 221 } 222 } 223 224 let body = translator.translate(&function.body); 225 let body = Self::flatten_expression(body); 226 let body = Self::remove_unused_code(body); 227 228 Function { 229 name, 230 body, 231 return_type, 232 parameters, 233 } 234 } 235 236 fn translate_type(type_: &type_::Type) -> Type { 237 match type_ { 238 type_::Type::Named { 239 publicity: _, 240 package: _, 241 module, 242 name, 243 arguments: _, 244 inferred_variant: _, 245 } => match (module.as_str(), name.as_str()) { 246 ("gleam", "Int") => Type::Int, 247 ("gleam", "Float") => Type::Float, 248 ("gleam", "Bool") => Type::Bool, 249 ("gleam", "String") => Type::String, 250 (_, _) => todo!(), 251 }, 252 type_::Type::Fn { 253 arguments: _, 254 return_: _, 255 } => todo!(), 256 type_::Type::Var { type_ } => match type_.borrow().deref() { 257 type_::TypeVar::Link { type_ } => Self::translate_type(type_), 258 type_::TypeVar::Unbound { id: _ } | type_::TypeVar::Generic { id: _ } => { 259 Type::Generic 260 } 261 }, 262 type_::Type::Tuple { elements: _ } => todo!(), 263 } 264 } 265 266 fn flatten_expression(expression: Expression) -> Expression { 267 match expression { 268 Expression::Block(expressions) => { 269 let mut block = vec![]; 270 271 for expression in expressions { 272 let expression = Self::flatten_expression(expression); 273 274 match expression { 275 Expression::Block(mut expressions) => block.append(&mut expressions), 276 Expression::Var { .. } 277 | Expression::FunctionRef { .. } 278 | Expression::Int { .. } 279 | Expression::Float { .. } 280 | Expression::Bool { .. } 281 | Expression::String { .. } 282 | Expression::Equals { .. } 283 | Expression::NotEquals { .. } 284 | Expression::IntGt { .. } 285 | Expression::IntGtEq { .. } 286 | Expression::IntLt { .. } 287 | Expression::IntLtEq { .. } 288 | Expression::IntAdd { .. } 289 | Expression::IntSub { .. } 290 | Expression::IntMul { .. } 291 | Expression::IntDiv { .. } 292 | Expression::FloatGt { .. } 293 | Expression::FloatGtEq { .. } 294 | Expression::FloatLt { .. } 295 | Expression::FloatLtEq { .. } 296 | Expression::FloatAdd { .. } 297 | Expression::FloatSub { .. } 298 | Expression::FloatMul { .. } 299 | Expression::FloatDiv { .. } 300 | Expression::StringConcat { .. } 301 | Expression::Set { .. } 302 | Expression::If { .. } 303 | Expression::Call { .. } => block.push(expression), 304 }; 305 } 306 307 match block.as_slice() { 308 [single] => single.clone(), 309 _ => Expression::Block(block), 310 } 311 } 312 Expression::FunctionRef { module, name } => Expression::FunctionRef { module, name }, 313 Expression::Var(var) => Expression::Var(var), 314 Expression::Int { value } => Expression::Int { value }, 315 Expression::Float { value } => Expression::Float { value }, 316 Expression::Bool { value } => Expression::Bool { value }, 317 Expression::String { value } => Expression::String { value }, 318 Expression::Equals { lhs, rhs } => Expression::Equals { 319 lhs: Box::new(Self::flatten_expression(*lhs)), 320 rhs: Box::new(Self::flatten_expression(*rhs)), 321 }, 322 Expression::NotEquals { lhs, rhs } => Expression::NotEquals { 323 lhs: Box::new(Self::flatten_expression(*lhs)), 324 rhs: Box::new(Self::flatten_expression(*rhs)), 325 }, 326 Expression::IntGt { lhs, rhs } => Expression::IntGt { 327 lhs: Box::new(Self::flatten_expression(*lhs)), 328 rhs: Box::new(Self::flatten_expression(*rhs)), 329 }, 330 Expression::IntGtEq { lhs, rhs } => Expression::IntGtEq { 331 lhs: Box::new(Self::flatten_expression(*lhs)), 332 rhs: Box::new(Self::flatten_expression(*rhs)), 333 }, 334 Expression::IntLt { lhs, rhs } => Expression::IntLt { 335 lhs: Box::new(Self::flatten_expression(*lhs)), 336 rhs: Box::new(Self::flatten_expression(*rhs)), 337 }, 338 Expression::IntLtEq { lhs, rhs } => Expression::IntLtEq { 339 lhs: Box::new(Self::flatten_expression(*lhs)), 340 rhs: Box::new(Self::flatten_expression(*rhs)), 341 }, 342 Expression::IntAdd { lhs, rhs } => Expression::IntAdd { 343 lhs: Box::new(Self::flatten_expression(*lhs)), 344 rhs: Box::new(Self::flatten_expression(*rhs)), 345 }, 346 Expression::IntSub { lhs, rhs } => Expression::IntSub { 347 lhs: Box::new(Self::flatten_expression(*lhs)), 348 rhs: Box::new(Self::flatten_expression(*rhs)), 349 }, 350 Expression::IntMul { lhs, rhs } => Expression::IntMul { 351 lhs: Box::new(Self::flatten_expression(*lhs)), 352 rhs: Box::new(Self::flatten_expression(*rhs)), 353 }, 354 Expression::IntDiv { lhs, rhs } => Expression::IntDiv { 355 lhs: Box::new(Self::flatten_expression(*lhs)), 356 rhs: Box::new(Self::flatten_expression(*rhs)), 357 }, 358 Expression::FloatGt { lhs, rhs } => Expression::FloatGt { 359 lhs: Box::new(Self::flatten_expression(*lhs)), 360 rhs: Box::new(Self::flatten_expression(*rhs)), 361 }, 362 Expression::FloatGtEq { lhs, rhs } => Expression::FloatGtEq { 363 lhs: Box::new(Self::flatten_expression(*lhs)), 364 rhs: Box::new(Self::flatten_expression(*rhs)), 365 }, 366 Expression::FloatLt { lhs, rhs } => Expression::FloatLt { 367 lhs: Box::new(Self::flatten_expression(*lhs)), 368 rhs: Box::new(Self::flatten_expression(*rhs)), 369 }, 370 Expression::FloatLtEq { lhs, rhs } => Expression::FloatLtEq { 371 lhs: Box::new(Self::flatten_expression(*lhs)), 372 rhs: Box::new(Self::flatten_expression(*rhs)), 373 }, 374 Expression::FloatAdd { lhs, rhs } => Expression::FloatAdd { 375 lhs: Box::new(Self::flatten_expression(*lhs)), 376 rhs: Box::new(Self::flatten_expression(*rhs)), 377 }, 378 Expression::FloatSub { lhs, rhs } => Expression::FloatSub { 379 lhs: Box::new(Self::flatten_expression(*lhs)), 380 rhs: Box::new(Self::flatten_expression(*rhs)), 381 }, 382 Expression::FloatMul { lhs, rhs } => Expression::FloatMul { 383 lhs: Box::new(Self::flatten_expression(*lhs)), 384 rhs: Box::new(Self::flatten_expression(*rhs)), 385 }, 386 Expression::FloatDiv { lhs, rhs } => Expression::FloatDiv { 387 lhs: Box::new(Self::flatten_expression(*lhs)), 388 rhs: Box::new(Self::flatten_expression(*rhs)), 389 }, 390 Expression::StringConcat { lhs, rhs } => Expression::StringConcat { 391 lhs: Box::new(Self::flatten_expression(*lhs)), 392 rhs: Box::new(Self::flatten_expression(*rhs)), 393 }, 394 Expression::Set { name, value } => Expression::Set { 395 name, 396 value: Box::new(Self::flatten_expression(*value)), 397 }, 398 Expression::If { cond, then, else_ } => Expression::If { 399 cond: Box::new(Self::flatten_expression(*cond)), 400 then: Box::new(Self::flatten_expression(*then)), 401 else_: Box::new(Self::flatten_expression(*else_)), 402 }, 403 Expression::Call { target, args } => Expression::Call { 404 target: Box::new(Self::flatten_expression(*target)), 405 args: args.into_iter().map(Self::flatten_expression).collect(), 406 }, 407 } 408 } 409 410 fn remove_unused_code(expression: Expression) -> Expression { 411 match expression { 412 Expression::Block(expressions) => { 413 let mut block = vec![]; 414 415 let last_expression_index = expressions.len() - 1; 416 417 for (index, expression) in expressions.into_iter().enumerate() { 418 let expression = Self::remove_unused_code(expression); 419 420 match expression { 421 Expression::Var { .. } 422 | Expression::FunctionRef { .. } 423 | Expression::Int { .. } 424 | Expression::Float { .. } 425 | Expression::Bool { .. } 426 | Expression::String { .. } 427 if index != last_expression_index => {} 428 429 Expression::Block(_) 430 | Expression::FunctionRef { .. } 431 | Expression::Var { .. } 432 | Expression::Int { .. } 433 | Expression::Float { .. } 434 | Expression::Bool { .. } 435 | Expression::String { .. } 436 | Expression::Equals { .. } 437 | Expression::NotEquals { .. } 438 | Expression::IntGt { .. } 439 | Expression::IntGtEq { .. } 440 | Expression::IntLt { .. } 441 | Expression::IntLtEq { .. } 442 | Expression::IntAdd { .. } 443 | Expression::IntSub { .. } 444 | Expression::IntMul { .. } 445 | Expression::IntDiv { .. } 446 | Expression::FloatGt { .. } 447 | Expression::FloatGtEq { .. } 448 | Expression::FloatLt { .. } 449 | Expression::FloatLtEq { .. } 450 | Expression::FloatAdd { .. } 451 | Expression::FloatSub { .. } 452 | Expression::FloatMul { .. } 453 | Expression::FloatDiv { .. } 454 | Expression::StringConcat { .. } 455 | Expression::Set { .. } 456 | Expression::If { .. } 457 | Expression::Call { .. } => block.push(expression), 458 } 459 } 460 461 match block.as_slice() { 462 [single] => single.clone(), 463 _ => Expression::Block(block), 464 } 465 } 466 Expression::FunctionRef { module, name } => Expression::FunctionRef { module, name }, 467 Expression::Var(var) => Expression::Var(var), 468 Expression::Int { value } => Expression::Int { value }, 469 Expression::Float { value } => Expression::Float { value }, 470 Expression::Bool { value } => Expression::Bool { value }, 471 Expression::String { value } => Expression::String { value }, 472 Expression::Equals { lhs, rhs } => Expression::Equals { 473 lhs: Box::new(Self::remove_unused_code(*lhs)), 474 rhs: Box::new(Self::remove_unused_code(*rhs)), 475 }, 476 Expression::NotEquals { lhs, rhs } => Expression::NotEquals { 477 lhs: Box::new(Self::remove_unused_code(*lhs)), 478 rhs: Box::new(Self::remove_unused_code(*rhs)), 479 }, 480 Expression::IntGt { lhs, rhs } => Expression::IntGt { 481 lhs: Box::new(Self::remove_unused_code(*lhs)), 482 rhs: Box::new(Self::remove_unused_code(*rhs)), 483 }, 484 Expression::IntGtEq { lhs, rhs } => Expression::IntGtEq { 485 lhs: Box::new(Self::remove_unused_code(*lhs)), 486 rhs: Box::new(Self::remove_unused_code(*rhs)), 487 }, 488 Expression::IntLt { lhs, rhs } => Expression::IntLt { 489 lhs: Box::new(Self::remove_unused_code(*lhs)), 490 rhs: Box::new(Self::remove_unused_code(*rhs)), 491 }, 492 Expression::IntLtEq { lhs, rhs } => Expression::IntLtEq { 493 lhs: Box::new(Self::remove_unused_code(*lhs)), 494 rhs: Box::new(Self::remove_unused_code(*rhs)), 495 }, 496 Expression::IntAdd { lhs, rhs } => Expression::IntAdd { 497 lhs: Box::new(Self::remove_unused_code(*lhs)), 498 rhs: Box::new(Self::remove_unused_code(*rhs)), 499 }, 500 Expression::IntSub { lhs, rhs } => Expression::IntSub { 501 lhs: Box::new(Self::remove_unused_code(*lhs)), 502 rhs: Box::new(Self::remove_unused_code(*rhs)), 503 }, 504 Expression::IntMul { lhs, rhs } => Expression::IntMul { 505 lhs: Box::new(Self::remove_unused_code(*lhs)), 506 rhs: Box::new(Self::remove_unused_code(*rhs)), 507 }, 508 Expression::IntDiv { lhs, rhs } => Expression::IntDiv { 509 lhs: Box::new(Self::remove_unused_code(*lhs)), 510 rhs: Box::new(Self::remove_unused_code(*rhs)), 511 }, 512 Expression::FloatGt { lhs, rhs } => Expression::FloatGt { 513 lhs: Box::new(Self::remove_unused_code(*lhs)), 514 rhs: Box::new(Self::remove_unused_code(*rhs)), 515 }, 516 Expression::FloatGtEq { lhs, rhs } => Expression::FloatGtEq { 517 lhs: Box::new(Self::remove_unused_code(*lhs)), 518 rhs: Box::new(Self::remove_unused_code(*rhs)), 519 }, 520 Expression::FloatLt { lhs, rhs } => Expression::FloatLt { 521 lhs: Box::new(Self::remove_unused_code(*lhs)), 522 rhs: Box::new(Self::remove_unused_code(*rhs)), 523 }, 524 Expression::FloatLtEq { lhs, rhs } => Expression::FloatLtEq { 525 lhs: Box::new(Self::remove_unused_code(*lhs)), 526 rhs: Box::new(Self::remove_unused_code(*rhs)), 527 }, 528 Expression::FloatAdd { lhs, rhs } => Expression::FloatAdd { 529 lhs: Box::new(Self::remove_unused_code(*lhs)), 530 rhs: Box::new(Self::remove_unused_code(*rhs)), 531 }, 532 Expression::FloatSub { lhs, rhs } => Expression::FloatSub { 533 lhs: Box::new(Self::remove_unused_code(*lhs)), 534 rhs: Box::new(Self::remove_unused_code(*rhs)), 535 }, 536 Expression::FloatMul { lhs, rhs } => Expression::FloatMul { 537 lhs: Box::new(Self::remove_unused_code(*lhs)), 538 rhs: Box::new(Self::remove_unused_code(*rhs)), 539 }, 540 Expression::FloatDiv { lhs, rhs } => Expression::FloatDiv { 541 lhs: Box::new(Self::remove_unused_code(*lhs)), 542 rhs: Box::new(Self::remove_unused_code(*rhs)), 543 }, 544 Expression::StringConcat { lhs, rhs } => Expression::StringConcat { 545 lhs: Box::new(Self::remove_unused_code(*lhs)), 546 rhs: Box::new(Self::remove_unused_code(*rhs)), 547 }, 548 Expression::Set { name, value } => Expression::Set { 549 name, 550 value: Box::new(Self::remove_unused_code(*value)), 551 }, 552 Expression::If { cond, then, else_ } => Expression::If { 553 cond: Box::new(Self::remove_unused_code(*cond)), 554 then: Box::new(Self::remove_unused_code(*then)), 555 else_: Box::new(Self::remove_unused_code(*else_)), 556 }, 557 Expression::Call { target, args } => Expression::Call { 558 target: Box::new(Self::remove_unused_code(*target)), 559 args: args.into_iter().map(Self::remove_unused_code).collect(), 560 }, 561 } 562 } 563} 564 565#[derive(Debug, Default)] 566pub struct BodyTranslator { 567 variables: HashMap<EcoString, EcoString>, 568 variable_count: usize, 569} 570 571#[derive(Debug, Clone, Copy, PartialEq, Eq)] 572enum DecisionKind { 573 Case, 574 Let, 575} 576 577impl BodyTranslator { 578 fn make_tmp_var(&mut self) -> Var { 579 self.make_var("_tmp".into()) 580 } 581 582 fn make_var(&mut self, name: EcoString) -> Var { 583 self.variable_count += 1; 584 585 let entry = eco_format!("{}${}", name.clone(), self.variable_count); 586 _ = self.variables.insert(name.clone(), entry.clone()); 587 588 Var { name: entry } 589 } 590 591 fn get_var(&mut self, name: &EcoString) -> Var { 592 let variable = self 593 .variables 594 .get(name) 595 .cloned() 596 .unwrap_or_else(|| panic!("variable '{name}' not found")); 597 598 Var { name: variable } 599 } 600 601 fn in_var_scope<T>(&mut self, func: impl Fn(&mut Self) -> T) -> T { 602 let snapshot = self.variables.clone(); 603 let result = func(self); 604 self.variables = snapshot; 605 result 606 } 607 608 fn translate(mut self, body: &Vec1<ast::TypedStatement>) -> Expression { 609 self.translate_statements(body) 610 } 611 612 fn translate_statements(&mut self, statements: &[ast::TypedStatement]) -> Expression { 613 Expression::Block(Iterator::collect( 614 statements.iter().map(|stmt| self.translate_statement(stmt)), 615 )) 616 } 617 618 fn translate_statement(&mut self, statement: &ast::TypedStatement) -> Expression { 619 match statement { 620 ast::Statement::Expression(expression) => self.translate_expression(expression), 621 ast::Statement::Assignment(assignment) => self.translate_assignment(assignment), 622 ast::Statement::Use(_) => todo!(), 623 ast::Statement::Assert(_) => todo!(), 624 } 625 } 626 627 fn translate_expression(&mut self, expression: &ast::TypedExpr) -> Expression { 628 match expression { 629 ast::TypedExpr::Int { 630 location: _, 631 type_: _, 632 value: _, 633 int_value, 634 } => Expression::Int { 635 value: int_value.clone(), 636 }, 637 ast::TypedExpr::Float { 638 location: _, 639 type_: _, 640 value, 641 } => Expression::Float { 642 value: value.clone(), 643 }, 644 ast::TypedExpr::String { 645 location: _, 646 type_: _, 647 value, 648 } => Expression::String { 649 value: value.clone(), 650 }, 651 ast::TypedExpr::Block { 652 location: _, 653 statements, 654 } => self.in_var_scope(|this| this.translate_statements(statements)), 655 ast::TypedExpr::Pipeline { 656 location: _, 657 first_value: _, 658 assignments: _, 659 finally: _, 660 finally_kind: _, 661 } => todo!(), 662 ast::TypedExpr::Var { 663 location: _, 664 constructor, 665 name, 666 } => match &constructor.variant { 667 type_::ValueConstructorVariant::LocalVariable { 668 location: _, 669 origin: _, 670 } => Expression::Var(self.get_var(name)), 671 type_::ValueConstructorVariant::ModuleConstant { 672 documentation: _, 673 location: _, 674 module: _, 675 name: _, 676 literal: _, 677 implementations: _, 678 } => todo!(), 679 type_::ValueConstructorVariant::LocalConstant { literal: _ } => todo!(), 680 type_::ValueConstructorVariant::ModuleFn { 681 name, 682 field_map: _, 683 module, 684 arity: _, 685 location: _, 686 documentation: _, 687 implementations: _, 688 external_erlang: _, 689 external_javascript: _, 690 external_cranelift: _, 691 purity: _, 692 } => Expression::FunctionRef { 693 module: module.clone(), 694 name: name.clone(), 695 }, 696 type_::ValueConstructorVariant::Record { 697 name, 698 arity: _, 699 field_map: _, 700 location: _, 701 module, 702 variants_count: _, 703 variant_index: _, 704 documentation: _, 705 } => match (module.as_str(), name.as_str()) { 706 ("gleam", "True") => Expression::Bool { value: true }, 707 ("gleam", "False") => Expression::Bool { value: false }, 708 (_, _) => todo!(), 709 }, 710 }, 711 ast::TypedExpr::Fn { 712 location: _, 713 type_: _, 714 kind: _, 715 arguments: _, 716 body: _, 717 return_annotation: _, 718 purity: _, 719 } => todo!(), 720 ast::TypedExpr::List { 721 location: _, 722 type_: _, 723 elements: _, 724 tail: _, 725 } => todo!(), 726 ast::TypedExpr::Call { 727 location: _, 728 type_: _, 729 fun, 730 arguments, 731 } => { 732 let target = self.translate_expression(fun); 733 let args = arguments 734 .iter() 735 .map(|arg| self.translate_expression(&arg.value)) 736 .collect(); 737 738 Expression::Call { 739 target: Box::new(target), 740 args, 741 } 742 } 743 ast::TypedExpr::BinOp { 744 location: _, 745 type_: _, 746 name, 747 name_location: _, 748 left, 749 right, 750 } => { 751 let lhs = Box::new(self.translate_expression(left)); 752 let rhs = Box::new(self.translate_expression(right)); 753 754 match name { 755 ast::BinOp::And => Expression::If { 756 cond: lhs, 757 then: rhs, 758 else_: Box::new(Expression::Bool { value: false }), 759 }, 760 ast::BinOp::Or => Expression::If { 761 cond: lhs, 762 then: Box::new(Expression::Bool { value: true }), 763 else_: rhs, 764 }, 765 ast::BinOp::Eq => Expression::Equals { lhs, rhs }, 766 ast::BinOp::NotEq => Expression::NotEquals { lhs, rhs }, 767 ast::BinOp::LtInt => Expression::IntLt { lhs, rhs }, 768 ast::BinOp::LtEqInt => Expression::IntLtEq { lhs, rhs }, 769 ast::BinOp::LtFloat => Expression::FloatLt { lhs, rhs }, 770 ast::BinOp::LtEqFloat => Expression::FloatLtEq { lhs, rhs }, 771 ast::BinOp::GtEqInt => Expression::IntGtEq { lhs, rhs }, 772 ast::BinOp::GtInt => Expression::IntGt { lhs, rhs }, 773 ast::BinOp::GtEqFloat => Expression::FloatGtEq { lhs, rhs }, 774 ast::BinOp::GtFloat => Expression::FloatGt { lhs, rhs }, 775 ast::BinOp::AddInt => Expression::IntAdd { lhs, rhs }, 776 ast::BinOp::AddFloat => Expression::FloatAdd { lhs, rhs }, 777 ast::BinOp::SubInt => Expression::IntSub { lhs, rhs }, 778 ast::BinOp::SubFloat => Expression::FloatSub { lhs, rhs }, 779 ast::BinOp::MultInt => Expression::IntMul { lhs, rhs }, 780 ast::BinOp::MultFloat => Expression::FloatMul { lhs, rhs }, 781 ast::BinOp::DivInt => Expression::IntDiv { lhs, rhs }, 782 ast::BinOp::DivFloat => Expression::FloatDiv { lhs, rhs }, 783 ast::BinOp::RemainderInt => todo!(), 784 ast::BinOp::Concatenate => Expression::StringConcat { lhs, rhs }, 785 } 786 } 787 ast::TypedExpr::Case { 788 location: _, 789 type_: _, 790 subjects, 791 clauses, 792 compiled_case, 793 } => { 794 let mut block = vec![]; 795 796 let subject_vars: Vec<_> = Iterator::collect(subjects.iter().map(|subject| { 797 let name = self.make_tmp_var(); 798 let value = self.translate_expression(subject); 799 800 block.push(Expression::Set { 801 name: name.clone(), 802 value: Box::new(value), 803 }); 804 805 return name; 806 })); 807 808 block.push(self.translate_decision( 809 DecisionKind::Case, 810 &subject_vars, 811 clauses, 812 &compiled_case.tree, 813 )); 814 815 Expression::Block(block) 816 } 817 ast::TypedExpr::RecordAccess { 818 location: _, 819 field_start: _, 820 type_: _, 821 label: _, 822 index: _, 823 record: _, 824 } => todo!(), 825 ast::TypedExpr::ModuleSelect { 826 location: _, 827 field_start: _, 828 type_: _, 829 label: _, 830 module_name: _, 831 module_alias: _, 832 constructor: _, 833 } => todo!(), 834 ast::TypedExpr::Tuple { 835 location: _, 836 type_: _, 837 elements: _, 838 } => todo!(), 839 ast::TypedExpr::TupleIndex { 840 location: _, 841 type_: _, 842 index: _, 843 tuple: _, 844 } => todo!(), 845 ast::TypedExpr::Todo { 846 location: _, 847 message: _, 848 kind: _, 849 type_: _, 850 } => todo!(), 851 ast::TypedExpr::Panic { 852 location: _, 853 message: _, 854 type_: _, 855 } => todo!(), 856 ast::TypedExpr::Echo { 857 location: _, 858 type_: _, 859 expression: _, 860 message: _, 861 } => todo!(), 862 ast::TypedExpr::BitArray { 863 location: _, 864 type_: _, 865 segments: _, 866 } => todo!(), 867 ast::TypedExpr::RecordUpdate { 868 location: _, 869 type_: _, 870 record_assignment: _, 871 constructor: _, 872 arguments: _, 873 } => todo!(), 874 ast::TypedExpr::NegateBool { location: _, value } => Expression::Equals { 875 lhs: Box::new(self.translate_expression(value)), 876 rhs: Box::new(Expression::Bool { value: false }), 877 }, 878 ast::TypedExpr::NegateInt { location: _, value } => Expression::IntSub { 879 lhs: Box::new(Expression::Int { 880 value: BigInt::from(0), 881 }), 882 rhs: Box::new(self.translate_expression(value)), 883 }, 884 ast::TypedExpr::Invalid { 885 location: _, 886 type_: _, 887 } => todo!(), 888 } 889 } 890 891 fn translate_assignment(&mut self, assignment: &ast::TypedAssignment) -> Expression { 892 let mut block = vec![]; 893 894 let value_var = self.make_tmp_var(); 895 let value = self.translate_expression(&assignment.value); 896 897 block.push(Expression::Set { 898 name: value_var.clone(), 899 value: Box::new(value), 900 }); 901 902 block.push(self.translate_decision( 903 DecisionKind::Let, 904 &[value_var.clone()], 905 &[], 906 &assignment.compiled_case.tree, 907 )); 908 909 block.push(Expression::Var(value_var)); 910 911 Expression::Block(block) 912 } 913 914 fn translate_decision( 915 &mut self, 916 kind: DecisionKind, 917 subjects: &[Var], 918 clauses: &[ast::TypedClause], 919 decision: &exhaustiveness::Decision, 920 ) -> Expression { 921 match decision { 922 exhaustiveness::Decision::Run { body } => { 923 let mut block = vec![]; 924 925 for (binding, value) in &body.bindings { 926 let binding = self.make_var(binding.clone()); 927 928 block.push(self.translate_binding(subjects, binding.clone(), value)); 929 } 930 931 if kind == DecisionKind::Case { 932 block.push(self.translate_expression(&clauses[body.clause_index].then)); 933 } 934 935 Expression::Block(block) 936 } 937 exhaustiveness::Decision::Guard { 938 guard, 939 if_true, 940 if_false, 941 } => { 942 let mut block = vec![]; 943 944 let guard = clauses[*guard] 945 .guard 946 .as_ref() 947 .expect("expected clause to have a guard"); 948 949 let referenced_in_guard = guard.referenced_variables(); 950 951 let (guard_bindings, if_true_bindings): (Vec<_>, Vec<_>) = if_true 952 .bindings 953 .iter() 954 .partition(|(binding, _)| referenced_in_guard.contains(binding)); 955 956 for (binding, value) in guard_bindings { 957 let binding = self.make_var(binding.clone()); 958 959 block.push(self.translate_binding(subjects, binding, value)); 960 } 961 962 let cond = self.translate_guard(guard); 963 964 let if_true = { 965 let mut block = vec![]; 966 967 for (binding, value) in if_true_bindings { 968 let binding = self.make_var(binding.clone()); 969 970 block.push(self.translate_binding(subjects, binding.clone(), value)); 971 } 972 973 block.push(self.translate_expression(&clauses[if_true.clause_index].then)); 974 block 975 }; 976 977 let if_false = self.translate_decision(kind, subjects, clauses, if_false); 978 979 block.push(Expression::If { 980 cond: Box::new(cond), 981 then: Box::new(Expression::Block(if_true)), 982 else_: Box::new(if_false), 983 }); 984 985 Expression::Block(block) 986 } 987 exhaustiveness::Decision::Switch { 988 var, 989 choices, 990 fallback, 991 fallback_check: _, 992 } => { 993 let fallback = self.translate_decision(kind, subjects, clauses, fallback); 994 995 DoubleEndedIterator::rfold(choices.iter(), fallback, |fallback, (check, then)| { 996 Expression::If { 997 cond: Box::new(self.translate_runtime_check( 998 check, 999 Expression::Var(subjects[var.id].clone()), 1000 )), 1001 then: Box::new(self.translate_decision(kind, subjects, clauses, then)), 1002 else_: Box::new(fallback), 1003 } 1004 }) 1005 } 1006 exhaustiveness::Decision::Fail => todo!(), 1007 } 1008 } 1009 1010 fn translate_guard(&mut self, guard: &ast::TypedClauseGuard) -> Expression { 1011 match guard { 1012 ast::ClauseGuard::Block { location: _, value } => self.translate_guard(value), 1013 ast::ClauseGuard::Equals { 1014 location: _, 1015 left, 1016 right, 1017 } => { 1018 let lhs = self.translate_guard(left); 1019 let rhs = self.translate_guard(right); 1020 1021 Expression::Equals { 1022 lhs: Box::new(lhs), 1023 rhs: Box::new(rhs), 1024 } 1025 } 1026 ast::ClauseGuard::NotEquals { 1027 location: _, 1028 left, 1029 right, 1030 } => Expression::NotEquals { 1031 lhs: Box::new(self.translate_guard(left)), 1032 rhs: Box::new(self.translate_guard(right)), 1033 }, 1034 ast::ClauseGuard::GtInt { 1035 location: _, 1036 left, 1037 right, 1038 } => Expression::IntGt { 1039 lhs: Box::new(self.translate_guard(left)), 1040 rhs: Box::new(self.translate_guard(right)), 1041 }, 1042 ast::ClauseGuard::GtEqInt { 1043 location: _, 1044 left, 1045 right, 1046 } => Expression::IntGtEq { 1047 lhs: Box::new(self.translate_guard(left)), 1048 rhs: Box::new(self.translate_guard(right)), 1049 }, 1050 ast::ClauseGuard::LtInt { 1051 location: _, 1052 left, 1053 right, 1054 } => Expression::IntLt { 1055 lhs: Box::new(self.translate_guard(left)), 1056 rhs: Box::new(self.translate_guard(right)), 1057 }, 1058 ast::ClauseGuard::LtEqInt { 1059 location: _, 1060 left, 1061 right, 1062 } => Expression::IntLtEq { 1063 lhs: Box::new(self.translate_guard(left)), 1064 rhs: Box::new(self.translate_guard(right)), 1065 }, 1066 ast::ClauseGuard::GtFloat { 1067 location: _, 1068 left, 1069 right, 1070 } => Expression::FloatGt { 1071 lhs: Box::new(self.translate_guard(left)), 1072 rhs: Box::new(self.translate_guard(right)), 1073 }, 1074 ast::ClauseGuard::GtEqFloat { 1075 location: _, 1076 left, 1077 right, 1078 } => Expression::FloatGtEq { 1079 lhs: Box::new(self.translate_guard(left)), 1080 rhs: Box::new(self.translate_guard(right)), 1081 }, 1082 ast::ClauseGuard::LtFloat { 1083 location: _, 1084 left, 1085 right, 1086 } => Expression::FloatLt { 1087 lhs: Box::new(self.translate_guard(left)), 1088 rhs: Box::new(self.translate_guard(right)), 1089 }, 1090 ast::ClauseGuard::LtEqFloat { 1091 location: _, 1092 left, 1093 right, 1094 } => Expression::FloatLtEq { 1095 lhs: Box::new(self.translate_guard(left)), 1096 rhs: Box::new(self.translate_guard(right)), 1097 }, 1098 ast::ClauseGuard::AddInt { 1099 location: _, 1100 left, 1101 right, 1102 } => Expression::IntAdd { 1103 lhs: Box::new(self.translate_guard(left)), 1104 rhs: Box::new(self.translate_guard(right)), 1105 }, 1106 ast::ClauseGuard::AddFloat { 1107 location: _, 1108 left, 1109 right, 1110 } => Expression::FloatAdd { 1111 lhs: Box::new(self.translate_guard(left)), 1112 rhs: Box::new(self.translate_guard(right)), 1113 }, 1114 ast::ClauseGuard::SubInt { 1115 location: _, 1116 left, 1117 right, 1118 } => Expression::IntSub { 1119 lhs: Box::new(self.translate_guard(left)), 1120 rhs: Box::new(self.translate_guard(right)), 1121 }, 1122 ast::ClauseGuard::SubFloat { 1123 location: _, 1124 left, 1125 right, 1126 } => Expression::FloatSub { 1127 lhs: Box::new(self.translate_guard(left)), 1128 rhs: Box::new(self.translate_guard(right)), 1129 }, 1130 ast::ClauseGuard::MultInt { 1131 location: _, 1132 left, 1133 right, 1134 } => Expression::IntMul { 1135 lhs: Box::new(self.translate_guard(left)), 1136 rhs: Box::new(self.translate_guard(right)), 1137 }, 1138 ast::ClauseGuard::MultFloat { 1139 location: _, 1140 left, 1141 right, 1142 } => Expression::FloatMul { 1143 lhs: Box::new(self.translate_guard(left)), 1144 rhs: Box::new(self.translate_guard(right)), 1145 }, 1146 ast::ClauseGuard::DivInt { 1147 location: _, 1148 left, 1149 right, 1150 } => Expression::IntDiv { 1151 lhs: Box::new(self.translate_guard(left)), 1152 rhs: Box::new(self.translate_guard(right)), 1153 }, 1154 ast::ClauseGuard::DivFloat { 1155 location: _, 1156 left, 1157 right, 1158 } => Expression::FloatDiv { 1159 lhs: Box::new(self.translate_guard(left)), 1160 rhs: Box::new(self.translate_guard(right)), 1161 }, 1162 ast::ClauseGuard::RemainderInt { 1163 location: _, 1164 left: _, 1165 right: _, 1166 } => todo!(), 1167 ast::ClauseGuard::Or { 1168 location: _, 1169 left, 1170 right, 1171 } => Expression::If { 1172 cond: Box::new(self.translate_guard(left)), 1173 then: Box::new(Expression::Bool { value: true }), 1174 else_: Box::new(self.translate_guard(right)), 1175 }, 1176 ast::ClauseGuard::And { 1177 location: _, 1178 left, 1179 right, 1180 } => Expression::If { 1181 cond: Box::new(self.translate_guard(left)), 1182 then: Box::new(self.translate_guard(right)), 1183 else_: Box::new(Expression::Bool { value: false }), 1184 }, 1185 ast::ClauseGuard::Not { 1186 location: _, 1187 expression, 1188 } => Expression::Equals { 1189 lhs: Box::new(self.translate_guard(expression)), 1190 rhs: Box::new(Expression::Bool { value: false }), 1191 }, 1192 ast::ClauseGuard::Var { 1193 location: _, 1194 type_: _, 1195 name, 1196 definition_location: _, 1197 } => Expression::Var(self.get_var(name)), 1198 ast::ClauseGuard::TupleIndex { 1199 location: _, 1200 index: _, 1201 type_: _, 1202 tuple: _, 1203 } => todo!(), 1204 ast::ClauseGuard::FieldAccess { 1205 label_location: _, 1206 index: _, 1207 label: _, 1208 type_: _, 1209 container: _, 1210 } => todo!(), 1211 ast::ClauseGuard::ModuleSelect { 1212 location: _, 1213 type_: _, 1214 label: _, 1215 module_name: _, 1216 module_alias: _, 1217 literal: _, 1218 } => todo!(), 1219 ast::ClauseGuard::Constant(constant) => self.translate_constant(constant), 1220 } 1221 } 1222 1223 fn translate_constant(&mut self, constant: &ast::TypedConstant) -> Expression { 1224 match constant { 1225 ast::Constant::Int { 1226 location: _, 1227 value: _, 1228 int_value, 1229 } => Expression::Int { 1230 value: int_value.clone(), 1231 }, 1232 ast::Constant::Float { location: _, value } => Expression::Float { 1233 value: value.clone(), 1234 }, 1235 ast::Constant::String { location: _, value } => Expression::String { 1236 value: value.clone(), 1237 }, 1238 ast::Constant::Tuple { 1239 location: _, 1240 elements: _, 1241 } => todo!(), 1242 ast::Constant::List { 1243 location: _, 1244 elements: _, 1245 type_: _, 1246 } => todo!(), 1247 ast::Constant::Record { 1248 location: _, 1249 module: _, 1250 name: _, 1251 arguments: _, 1252 tag: _, 1253 type_: _, 1254 field_map: _, 1255 record_constructor: _, 1256 } => todo!(), 1257 ast::Constant::BitArray { 1258 location: _, 1259 segments: _, 1260 } => todo!(), 1261 ast::Constant::Var { 1262 location: _, 1263 module: _, 1264 name: _, 1265 constructor: _, 1266 type_: _, 1267 } => todo!(), 1268 ast::Constant::StringConcatenation { 1269 location: _, 1270 left: _, 1271 right: _, 1272 } => todo!(), 1273 ast::Constant::Invalid { 1274 location: _, 1275 type_: _, 1276 } => todo!(), 1277 } 1278 } 1279 1280 fn translate_binding( 1281 &mut self, 1282 subjects: &[Var], 1283 binding: Var, 1284 value: &exhaustiveness::BoundValue, 1285 ) -> Expression { 1286 let value = match value { 1287 exhaustiveness::BoundValue::Variable(variable) => { 1288 Expression::Var(subjects[variable.id].clone()) 1289 } 1290 exhaustiveness::BoundValue::LiteralString(_eco_string) => todo!(), 1291 exhaustiveness::BoundValue::LiteralInt(big_int) => Expression::Int { 1292 value: big_int.clone(), 1293 }, 1294 exhaustiveness::BoundValue::LiteralFloat(_eco_string) => todo!(), 1295 exhaustiveness::BoundValue::BitArraySlice { 1296 bit_array: _, 1297 read_action: _, 1298 } => todo!(), 1299 }; 1300 1301 Expression::Set { 1302 name: binding, 1303 value: Box::new(value), 1304 } 1305 } 1306 1307 fn translate_runtime_check( 1308 &mut self, 1309 check: &exhaustiveness::RuntimeCheck, 1310 against: Expression, 1311 ) -> Expression { 1312 match check { 1313 exhaustiveness::RuntimeCheck::Int { value } => { 1314 let value = parse::parse_int_value(&value).expect("unable to parse integer value"); 1315 1316 Expression::Equals { 1317 lhs: Box::new(against), 1318 rhs: Box::new(Expression::Int { value }), 1319 } 1320 } 1321 exhaustiveness::RuntimeCheck::Float { value: _ } => todo!(), 1322 exhaustiveness::RuntimeCheck::String { value: _ } => todo!(), 1323 exhaustiveness::RuntimeCheck::StringPrefix { prefix: _, rest: _ } => todo!(), 1324 exhaustiveness::RuntimeCheck::Tuple { 1325 size: _, 1326 elements: _, 1327 } => todo!(), 1328 exhaustiveness::RuntimeCheck::BitArray { test: _ } => todo!(), 1329 exhaustiveness::RuntimeCheck::Variant { 1330 match_: _, 1331 index: _, 1332 labels: _, 1333 fields: _, 1334 } => todo!(), 1335 exhaustiveness::RuntimeCheck::NonEmptyList { first: _, rest: _ } => todo!(), 1336 exhaustiveness::RuntimeCheck::EmptyList => todo!(), 1337 } 1338 } 1339} 1340 1341#[cfg(test)] 1342mod tests { 1343 use std::collections::{HashMap, HashSet}; 1344 1345 use super::*; 1346 use crate::analyse::TargetSupport; 1347 use crate::build::{Origin, Target}; 1348 use crate::config::PackageConfig; 1349 use crate::line_numbers::LineNumbers; 1350 use crate::parse::parse_module; 1351 use crate::type_::PRELUDE_MODULE_NAME; 1352 use crate::uid::UniqueIdGenerator; 1353 use crate::warning::{TypeWarningEmitter, WarningEmitter}; 1354 use camino::Utf8PathBuf; 1355 1356 fn compile_module(src: &str) -> ast::TypedModule { 1357 use crate::type_::build_prelude; 1358 let parsed = parse_module(Utf8PathBuf::from("test/path"), src, &WarningEmitter::null()) 1359 .expect("syntax error"); 1360 let ast = parsed.module; 1361 let ids = UniqueIdGenerator::new(); 1362 let mut config = PackageConfig::default(); 1363 config.name = "thepackage".into(); 1364 let mut modules = im::HashMap::new(); 1365 let _ = modules.insert(PRELUDE_MODULE_NAME.into(), build_prelude(&ids)); 1366 let line_numbers = LineNumbers::new(src); 1367 1368 crate::analyse::ModuleAnalyzerConstructor::<()> { 1369 target: Target::Erlang, 1370 ids: &ids, 1371 origin: Origin::Src, 1372 importable_modules: &modules, 1373 warnings: &TypeWarningEmitter::null(), 1374 direct_dependencies: &HashMap::new(), 1375 dev_dependencies: &HashSet::new(), 1376 target_support: TargetSupport::Enforced, 1377 package_config: &config, 1378 } 1379 .infer_module(ast, line_numbers, "".into()) 1380 .expect("should successfully infer") 1381 } 1382 1383 fn translate(src: &str) -> Module { 1384 let typed_module = compile_module(src); 1385 1386 Translator::default().translate(&typed_module) 1387 } 1388 1389 #[test] 1390 fn translates_samples() { 1391 insta::assert_debug_snapshot!(translate( 1392 r#"pub fn add(lhs: Int, rhs: Int) -> Int { 1393 lhs + rhs 1394 }"# 1395 )); 1396 1397 insta::assert_debug_snapshot!(translate( 1398 r#"pub fn some_case(n: Int) -> Int { 1399 case n { 1400 0 -> 2 1401 1 -> 4 1402 _ -> 6 1403 } 1404 }"# 1405 )); 1406 1407 insta::assert_debug_snapshot!(translate( 1408 r#"pub fn some_case(n: Int) -> Int { 1409 case n { 1410 y if y == 0 -> 2 1411 1 -> 4 1412 _ -> 6 1413 } 1414 }"# 1415 )); 1416 1417 insta::assert_debug_snapshot!(translate( 1418 r#"pub fn fib(n: Int) -> Int { 1419 case n { 1420 0 -> 0 1421 1 -> 1 1422 _ -> fib(n - 1) + fib(n - 2) 1423 } 1424 }"# 1425 )); 1426 1427 insta::assert_debug_snapshot!(translate( 1428 r#"pub fn maths() { 1429 let _ = 1 + 0 1430 let _ = 2 - 1 1431 let _ = 3 * 2 1432 let _ = 4 / 3 1433 1434 let _ = 1.0 +. 0.0 1435 let _ = 2.0 -. 1.0 1436 let _ = 3.0 *. 2.0 1437 let _ = 4.0 /. 3.0 1438 1439 let _ = 1 == 2 && 2 == 4 1440 let _ = 1 == 2 || 2 == 4 1441 }"# 1442 )); 1443 1444 insta::assert_debug_snapshot!(translate( 1445 r#"pub fn booleans() { 1446 let _ = True 1447 let _ = False 1448 1449 let _ = True == False 1450 let _ = False != True 1451 1452 let _ = 1 > 2 1453 let _ = 1 >= 2 1454 let _ = 1 < 2 1455 let _ = 1 <= 2 1456 1457 let _ = 1.0 >. 2.0 1458 let _ = 1.0 >=. 2.0 1459 let _ = 1.0 <. 2.0 1460 let _ = 1.0 <=. 2.0 1461 }"# 1462 )); 1463 1464 insta::assert_debug_snapshot!(translate( 1465 r#"pub fn guard_operations(n: Int, f: Float) -> Int { 1466 case n, f { 1467 x, y if x == 0 -> 1 1468 x, y if x != 1 -> 2 1469 x, y if x > 2 -> 3 1470 x, y if x >= 3 -> 4 1471 x, y if x < 4 -> 5 1472 x, y if x <= 5 -> 6 1473 x, y if y >. 1.0 -> 7 1474 x, y if y >=. 2.0 -> 8 1475 x, y if y <. 3.0 -> 9 1476 x, y if y <=. 4.0 -> 10 1477 x, y if x > 0 && x < 10 -> 11 1478 x, y if x < 0 || x > 100 -> 12 1479 x, y if !{ x == 0 } -> 13 1480 x, y if x + 1 > 5 -> 14 1481 x, y if x - 1 < 0 -> 15 1482 x, y if x * 2 == 10 -> 16 1483 x, y if x / 2 == 1 -> 17 1484 x, y if y +. 1.0 >. 5.0 -> 18 1485 x, y if y -. 1.0 <. 0.0 -> 19 1486 x, y if y *. 2.0 == 4.0 -> 20 1487 x, y if y /. 2.0 >=. 1.0 -> 21 1488 _, _ -> 0 1489 } 1490 }"# 1491 )); 1492 1493 insta::assert_debug_snapshot!(translate( 1494 r#"pub fn strings() { 1495 let _ = "hello" 1496 let _ = "hello, " <> "world!" 1497 }"# 1498 )); 1499 } 1500}