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