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