swamp_code_gen/
statement.rs

1/*
2 * Copyright (c) Peter Bjorklund. All rights reserved. https://github.com/swamp/swamp
3 * Licensed under the MIT License. See LICENSE in the project root for license information.
4 */
5use crate::code_bld::CodeBuilder;
6use crate::ctx::Context;
7use crate::transformer::{Collection, Transformer};
8use source_map_node::Node;
9use swamp_semantic::{BooleanExpression, Expression, ForPattern, Iterable};
10use swamp_types::TypeKind;
11use swamp_types::TypeRef;
12use swamp_vm_types::types::{Place, TypedRegister};
13
14impl CodeBuilder<'_> {
15    pub fn emit_statement(&mut self, expr: &Expression, ctx: &Context) {
16        debug_assert!(matches!(&*expr.ty.kind, TypeKind::Unit | TypeKind::Never));
17        let output_destination = Place::new_unit();
18        self.emit_expression(&output_destination, expr, ctx);
19    }
20
21    pub(crate) fn emit_for_loop(
22        &mut self,
23        destination: &Place,
24        node: &Node,
25        for_pattern: &ForPattern,
26        iterable: &Iterable,
27        lambda_non_capturing_expr: &Expression,
28        ctx: &Context,
29    ) {
30        // Add check if the collection is empty, to skip everything
31
32        // get some kind of iteration pointer
33
34        // check if it has reached its end
35
36        let collection_type = &iterable.resolved_expression.ty;
37        let hwm = self.temp_registers.save_mark();
38
39        // Check if any of the lambda variables are mutable
40        // If so, we need persistent storage for the collection (not temporary)
41        let variables_are_mutable = match for_pattern {
42            ForPattern::Single(var) => var.is_mutable(),
43            ForPattern::Pair(var1, var2) => var1.is_mutable() || var2.is_mutable(),
44        };
45        let allow_temporary = !variables_are_mutable;
46
47        let collection_ptr_reg = self.emit_scalar_rvalue_or_pointer_to_temporary(
48            &iterable.resolved_expression,
49            ctx,
50            allow_temporary,
51        );
52        let underlying_collection = collection_type;
53        match &*underlying_collection.kind {
54            TypeKind::StackStorage(_element_type, _)
55            | TypeKind::StackView(_element_type)
56            | TypeKind::QueueStorage(_element_type, _)
57            | TypeKind::QueueView(_element_type)
58            | TypeKind::DynamicLengthVecView(_element_type)
59            | TypeKind::VecStorage(_element_type, ..)
60            | TypeKind::FixedCapacityAndLengthArray(_element_type, _)
61            | TypeKind::SliceView(_element_type) => {
62                self.emit_for_loop_lambda(
63                    destination,
64                    node,
65                    Collection::Vec,
66                    &collection_ptr_reg,
67                    collection_type,
68                    for_pattern,
69                    lambda_non_capturing_expr,
70                    ctx,
71                );
72            }
73            TypeKind::SparseStorage(element_type, _) | TypeKind::SparseView(element_type) => {
74                self.emit_for_loop_lambda(
75                    destination,
76                    node,
77                    Collection::Sparse,
78                    &collection_ptr_reg,
79                    collection_type,
80                    for_pattern,
81                    lambda_non_capturing_expr,
82                    ctx,
83                );
84            }
85            TypeKind::DynamicLengthMapView(_key, _value)
86            | TypeKind::MapStorage(_key, _value, ..) => {
87                self.emit_for_loop_lambda(
88                    destination,
89                    node,
90                    Collection::Map,
91                    &collection_ptr_reg,
92                    collection_type,
93                    for_pattern,
94                    lambda_non_capturing_expr,
95                    ctx,
96                );
97            }
98            TypeKind::StringView(..) | TypeKind::StringStorage(..) => {
99                self.emit_for_loop_lambda(
100                    destination,
101                    node,
102                    Collection::String,
103                    &collection_ptr_reg,
104                    collection_type,
105                    for_pattern,
106                    lambda_non_capturing_expr,
107                    ctx,
108                );
109            }
110
111            _ => {
112                panic!("can not iterate this collection {underlying_collection}");
113            }
114        }
115
116        self.temp_registers.restore_to_mark(hwm);
117    }
118
119    fn emit_for_loop_lambda(
120        &mut self,
121        target_reg: &Place,
122        node: &Node,
123        collection: Collection,
124        source_collection: &TypedRegister,
125        source_collection_type: &TypeRef,
126        for_pattern: &ForPattern,
127        lambda_expr: &Expression,
128        ctx: &Context,
129    ) {
130        let variables = match for_pattern {
131            ForPattern::Single(a) => vec![a.clone()],
132            ForPattern::Pair(a, b) => vec![a.clone(), b.clone()],
133        };
134
135        self.emit_iterate_over_collection_with_lambda(
136            target_reg,
137            node,
138            collection,
139            Transformer::For,
140            source_collection,
141            (variables, lambda_expr),
142            ctx,
143        );
144    }
145
146    pub(crate) fn emit_while_loop(
147        &mut self,
148        condition: &BooleanExpression,
149        expression: &Expression,
150        ctx: &Context,
151    ) {
152        // `while` loops are only for side effects, make sure that the target size is zero (Unit)
153        //assert_eq!(target_reg.size.0, 0);
154
155        let ip_for_condition = self.builder.position();
156
157        let jump_on_false_condition = self.emit_condition_context(condition, ctx);
158
159        // Expression is only for side effects
160        self.emit_statement(expression, ctx);
161
162        // Always jump to the condition again to see if it is true
163        self.builder
164            .add_jmp(ip_for_condition, &expression.node, "jmp to while condition");
165
166        self.builder.patch_jump_here(jump_on_false_condition);
167    }
168}