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