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