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