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