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