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