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