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::Range(_range_struct_ref) => {
55                self.emit_for_loop_lambda(
56                    destination,
57                    node,
58                    Collection::Range,
59                    &collection_ptr_reg,
60                    collection_type,
61                    for_pattern,
62                    lambda_non_capturing_expr,
63                    ctx,
64                );
65            }
66
67            TypeKind::StackStorage(_element_type, _)
68            | TypeKind::StackView(_element_type)
69            | TypeKind::QueueStorage(_element_type, _)
70            | TypeKind::QueueView(_element_type)
71            | TypeKind::DynamicLengthVecView(_element_type)
72            | TypeKind::VecStorage(_element_type, ..)
73            | TypeKind::FixedCapacityAndLengthArray(_element_type, _)
74            | TypeKind::SliceView(_element_type) => {
75                self.emit_for_loop_lambda(
76                    destination,
77                    node,
78                    Collection::Vec,
79                    &collection_ptr_reg,
80                    collection_type,
81                    for_pattern,
82                    lambda_non_capturing_expr,
83                    ctx,
84                );
85            }
86            TypeKind::SparseStorage(element_type, _) | TypeKind::SparseView(element_type) => {
87                self.emit_for_loop_lambda(
88                    destination,
89                    node,
90                    Collection::Sparse,
91                    &collection_ptr_reg,
92                    collection_type,
93                    for_pattern,
94                    lambda_non_capturing_expr,
95                    ctx,
96                );
97            }
98            TypeKind::DynamicLengthMapView(_key, _value)
99            | TypeKind::MapStorage(_key, _value, ..) => {
100                self.emit_for_loop_lambda(
101                    destination,
102                    node,
103                    Collection::Map,
104                    &collection_ptr_reg,
105                    collection_type,
106                    for_pattern,
107                    lambda_non_capturing_expr,
108                    ctx,
109                );
110            }
111            TypeKind::StringView(..) | TypeKind::StringStorage(..) => {
112                self.emit_for_loop_lambda(
113                    destination,
114                    node,
115                    Collection::String,
116                    &collection_ptr_reg,
117                    collection_type,
118                    for_pattern,
119                    lambda_non_capturing_expr,
120                    ctx,
121                );
122            }
123
124            _ => {
125                panic!("can not iterate this collection {underlying_collection}");
126            }
127        }
128
129        self.temp_registers.restore_to_mark(hwm);
130    }
131
132    fn emit_for_loop_lambda(
133        &mut self,
134        target_reg: &Place,
135        node: &Node,
136        collection: Collection,
137        source_collection: &TypedRegister,
138        source_collection_type: &TypeRef,
139        for_pattern: &ForPattern,
140        lambda_expr: &Expression,
141        ctx: &Context,
142    ) {
143        let variables = match for_pattern {
144            ForPattern::Single(a) => vec![a.clone()],
145            ForPattern::Pair(a, b) => vec![a.clone(), b.clone()],
146        };
147
148        self.emit_iterate_over_collection_with_lambda(
149            target_reg,
150            node,
151            collection,
152            Transformer::For,
153            source_collection,
154            (variables, lambda_expr),
155            ctx,
156        );
157    }
158
159    pub(crate) fn emit_while_loop(
160        &mut self,
161        condition: &BooleanExpression,
162        expression: &Expression,
163        ctx: &Context,
164    ) {
165        // `while` loops are only for side effects, make sure that the target size is zero (Unit)
166        //assert_eq!(target_reg.size.0, 0);
167
168        let ip_for_condition = self.builder.position();
169
170        let jump_on_false_condition = self.emit_condition_context(condition, ctx);
171
172        // Expression is only for side effects
173        self.emit_statement(expression, ctx);
174
175        // Always jump to the condition again to see if it is true
176        self.builder
177            .add_jmp(ip_for_condition, &expression.node, "jmp to while condition");
178
179        self.builder.patch_jump_here(jump_on_false_condition);
180    }
181}