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