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