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