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::{Destination, 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 = Destination::new_unit();
18        self.emit_expression(&output_destination, expr, ctx);
19    }
20
21    pub(crate) fn emit_for_loop(
22        &mut self,
23        destination: &Destination,
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) | TypeKind::MapStorage(_key, _value, ..) => {
99                self.emit_for_loop_lambda(
100                    destination,
101                    node,
102                    Collection::Map,
103                    &collection_ptr_reg,
104                    collection_type,
105                    for_pattern,
106                    lambda_non_capturing_expr,
107                    ctx,
108                );
109            }
110            TypeKind::String(..) => {
111                self.emit_for_loop_lambda(
112                    destination,
113                    node,
114                    Collection::String,
115                    &collection_ptr_reg,
116                    collection_type,
117                    for_pattern,
118                    lambda_non_capturing_expr,
119                    ctx,
120                );
121            }
122
123            _ => {
124                panic!("can not iterate this collection {underlying_collection}");
125            }
126        }
127
128        self.temp_registers.restore_to_mark(hwm);
129    }
130
131    fn emit_for_loop_lambda(
132        &mut self,
133        target_reg: &Destination,
134        node: &Node,
135        collection: Collection,
136        source_collection: &TypedRegister,
137        source_collection_type: &TypeRef,
138        for_pattern: &ForPattern,
139        lambda_expr: &Expression,
140        ctx: &Context,
141    ) {
142        let variables = match for_pattern {
143            ForPattern::Single(a) => vec![a.clone()],
144            ForPattern::Pair(a, b) => vec![a.clone(), b.clone()],
145        };
146
147        self.emit_iterate_over_collection_with_lambda(
148            target_reg,
149            node,
150            collection,
151            Transformer::For,
152            source_collection,
153            (variables, lambda_expr),
154            ctx,
155        );
156    }
157
158    pub(crate) fn emit_while_loop(
159        &mut self,
160        condition: &BooleanExpression,
161        expression: &Expression,
162        ctx: &Context,
163    ) {
164        // `while` loops are only for side effects, make sure that the target size is zero (Unit)
165        //assert_eq!(target_reg.size.0, 0);
166
167        let ip_for_condition = self.builder.position();
168
169        let jump_on_false_condition = self.emit_condition_context(condition, ctx);
170
171        // Expression is only for side effects
172        self.emit_statement(expression, ctx);
173
174        // Always jump to the condition again to see if it is true
175        self.builder
176            .add_jmp(ip_for_condition, &expression.node, "jmp to while condition");
177
178        self.builder.patch_jump_here(jump_on_false_condition);
179    }
180}