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