Fork of daniellemaywood.uk/gleam — Wasm codegen work
43 kB
1246 lines
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}