1use crate::FlagStateKind;
6use crate::code_bld::CodeBuilder;
7use crate::ctx::Context;
8use crate::transformer::{Collection, Transformer, TransformerResult};
9use source_map_node::Node;
10use swamp_semantic::{Expression, VariableRef};
11use swamp_vm_isa::{InstructionPosition, MemoryOffset};
12use swamp_vm_types::types::{
13 BasicTypeKind, BasicTypeRef, Place, TypedRegister, VmType, pointer_type, u8_type, u32_type,
14};
15use swamp_vm_types::{MemoryLocation, PatchPosition};
16use tracing::error;
17
18impl CodeBuilder<'_> {
19 #[allow(clippy::too_many_lines)]
52 #[allow(clippy::too_many_arguments)]
53 pub fn emit_iterate_over_collection_with_lambda(
54 &mut self,
55 target_destination: &Place,
56 node: &Node,
57 source_collection_type: Collection,
58 transformer: Transformer,
59 source_collection_reg: &TypedRegister,
60 lambda_tuple: (Vec<VariableRef>, &Expression),
61 ctx: &Context,
62 ) {
63 let (lambda_variables, lambda_expr) = lambda_tuple;
64 let maybe_primary_element_gen_type = source_collection_reg.ty.basic_type.element();
65
66 let target_variables: Vec<_> = lambda_variables
67 .iter()
68 .map(|x| {
69 self.variable_registers
70 .get(&x.unique_id_within_function)
71 .unwrap()
72 .clone()
73 })
74 .collect();
75
76 let primary_variable = &target_variables[target_variables.len() - 1];
78
79 let hwm = self.temp_registers.save_mark();
80
81 let maybe_target_collection_pointer = match transformer.return_type() {
83 TransformerResult::VecFromSourceCollection | TransformerResult::VecWithLambdaResult => {
84 let target_memory_location = target_destination.memory_location_or_pointer_reg();
87
88 self.emit_initialize_memory_for_any_type(
89 &target_memory_location,
90 node,
91 "initialize target collection for transformer",
92 );
93
94 if matches!(
97 transformer.return_type(),
98 TransformerResult::VecFromSourceCollection
99 ) {
100 let computed_pointer = self.emit_compute_effective_address_to_register(
101 target_destination,
102 node,
103 "get pointer to collection (before lambda execution)",
104 );
105
106 Some(computed_pointer)
107 } else {
108 None
109 }
110 }
111 _ => {
112 None
114 }
115 };
116
117 let (continue_iteration_label, iteration_complete_patch_position) = self
119 .emit_iter_init_and_next(
120 node,
121 source_collection_type,
122 maybe_primary_element_gen_type,
123 source_collection_reg,
124 &target_variables,
125 );
126
127 let lambda_result = self.emit_scalar_rvalue(lambda_expr, ctx);
129
130 let transformer_t_flag_state = self.check_if_transformer_sets_t_flag(transformer);
132
133 let maybe_skip_early = if self.check_if_transformer_can_skip_early(transformer)
135 && matches!(
136 transformer_t_flag_state,
137 FlagStateKind::TFlagIsTrueWhenSet | FlagStateKind::TFlagIsTrueWhenClear
138 ) {
139 let skip_early = self.builder.add_jmp_if_not_equal_polarity_placeholder(
141 &lambda_result,
142 &transformer_t_flag_state.polarity(),
143 node,
144 "skip early",
145 );
146
147 Some(skip_early)
148 } else {
149 None
151 };
152
153 match transformer.return_type() {
156 TransformerResult::Unit => {
157
158 }
160 TransformerResult::Bool => {
161 }
163 TransformerResult::WrappedValueFromSourceCollection => {
164 }
166 TransformerResult::VecWithLambdaResult => {
167 self.transformer_add_to_collection(
168 &lambda_result,
169 transformer.needs_tag_removed(),
170 source_collection_type,
171 target_destination.register().unwrap(),
172 node,
173 );
174 }
175 TransformerResult::VecFromSourceCollection => {
176 let skip_if_false_patch_position = self.builder.add_jmp_if_not_true_placeholder(
177 &lambda_result,
178 node,
179 "skip the result if it is false",
180 );
181
182 let absolute_pointer = maybe_target_collection_pointer
183 .as_ref()
184 .expect("collection pointer should have been computed before lambda execution");
185
186 self.add_to_collection(
187 node,
188 source_collection_type,
189 absolute_pointer,
190 primary_variable,
191 );
192
193 self.builder.patch_jump_here(skip_if_false_patch_position);
194 }
195 }
196
197 self.builder.add_jmp(
199 continue_iteration_label,
200 &lambda_expr.debug_last_expression().node,
201 "jump to iter_next",
202 );
203
204 self.builder
205 .patch_jump_here(iteration_complete_patch_position);
206
207 if let Some(found_skip_early) = maybe_skip_early {
209 self.builder.patch_jump_here(found_skip_early);
210 }
211
212 match transformer.return_type() {
213 TransformerResult::Bool => {
214 }
216 TransformerResult::WrappedValueFromSourceCollection => {
217 let destination_type = target_destination.ty();
218 let BasicTypeKind::Optional(tagged_union) = &destination_type.kind else {
219 panic!("expected optional {destination_type:?}");
220 };
221
222 let tag_some_value_reg = self.temp_registers.allocate(
223 VmType::new_unknown_placement(u8_type()),
224 "iterate over collection target",
225 );
226
227 self.builder.add_mov8_immediate(
228 tag_some_value_reg.register(),
229 1,
230 node,
231 "mark tag as Some",
232 );
233
234 let result_location = target_destination.memory_location_or_pointer_reg();
235
236 self.builder.add_st8_using_ptr_with_offset(
237 &result_location.unsafe_add_offset(tagged_union.tag_offset),
238 tag_some_value_reg.register(),
239 node,
240 "store Tag value of (1) into memory",
241 );
242
243 let payload_memory_location = result_location.unsafe_add_offset(MemoryOffset(4));
245 let payload_destination = Place::Memory(payload_memory_location);
246
247 let source_location = Place::Register(primary_variable.clone());
248
249 self.emit_store_value_to_memory_place(
250 &payload_destination,
251 &source_location,
252 node,
253 "copy result in place",
254 );
255 }
256 _ => {}
257 }
258
259 self.temp_registers.restore_to_mark(hwm);
260 }
261
262 #[allow(clippy::unnecessary_wraps)]
263 #[allow(clippy::too_many_lines)]
264 fn emit_iter_init_and_next(
265 &mut self,
266 node: &Node,
267 collection_type: Collection,
268 maybe_element_type: Option<BasicTypeRef>,
269 collection_self_addr: &TypedRegister,
270 target_variables: &[TypedRegister],
271 ) -> (InstructionPosition, PatchPosition) {
272 let iterator_gen_type = collection_type.iterator_gen_type();
273
274 let target_iterator_header_reg = self.allocate_frame_space_and_return_absolute_pointer_reg(
275 &iterator_gen_type,
276 node,
277 "allocate iterator header space",
278 );
279
280 let primary_register = if target_variables.len() == 2 {
281 &target_variables[1]
282 } else {
283 &target_variables[0]
284 };
285
286 let iter_next_position = InstructionPosition(self.builder.position().0 + 1);
287 let placeholder = match collection_type {
288 Collection::Range => {
289 self.builder.add_range_iter_init(
290 &target_iterator_header_reg,
291 collection_self_addr,
292 node,
293 "range init",
294 );
295
296 assert_eq!(target_variables.len(), 1);
297 self.builder.add_range_iter_next_placeholder(
298 &target_iterator_header_reg,
299 &target_variables[0],
300 node,
301 "range iter next single",
302 )
303 }
304
305 Collection::Map => {
306 let (temp_registers, target_variables_to_use) = if target_variables.len() == 2 {
312 let key_register = &target_variables[0];
314 let value_register = &target_variables[1];
315
316 let (key_is_scalar, value_is_scalar) =
317 match &collection_self_addr.ty.basic_type.kind {
318 BasicTypeKind::MapStorage {
319 key_type,
320 value_type,
321 ..
322 } => (key_type.is_scalar(), value_type.is_scalar()),
323 BasicTypeKind::DynamicLengthMapView(key_item, value_item) => {
324 (key_item.ty.is_scalar(), value_item.ty.is_scalar())
325 }
326 _ => (false, false),
327 };
328
329 let mut temp_regs = Vec::new();
330 let mut target_vars = Vec::new();
331
332 if key_is_scalar {
334 let temp_key_addr = self
335 .temp_registers
336 .allocate(key_register.ty.clone(), "temp address for key");
337 temp_regs.push((temp_key_addr.register().clone(), key_register.clone()));
338 target_vars.push(temp_key_addr.register);
339 } else {
340 target_vars.push(key_register.clone());
341 }
342
343 if value_is_scalar {
345 let temp_value_addr = self
346 .temp_registers
347 .allocate(value_register.ty.clone(), "temp address for value");
348 temp_regs
349 .push((temp_value_addr.register().clone(), value_register.clone()));
350 target_vars.push(temp_value_addr.register);
351 } else {
352 target_vars.push(value_register.clone());
353 }
354
355 (Some(temp_regs), target_vars)
356 } else {
357 let value_register = &target_variables[0];
359 let value_is_scalar = match &collection_self_addr.ty.basic_type.kind {
360 BasicTypeKind::MapStorage { value_type, .. } => value_type.is_scalar(),
361 BasicTypeKind::DynamicLengthMapView(_, value_item) => {
362 value_item.ty.is_scalar()
363 }
364 _ => false,
365 };
366
367 if value_is_scalar {
368 let temp_value_addr = self
369 .temp_registers
370 .allocate(value_register.ty.clone(), "temp address for value");
371 (
372 Some(vec![(
373 temp_value_addr.register().clone(),
374 value_register.clone(),
375 )]),
376 vec![temp_value_addr.register.clone()],
377 )
378 } else {
379 (None, target_variables.to_vec())
380 }
381 };
382
383 let patch_position = self.emit_iter_init_and_next_to_memory(
384 &target_iterator_header_reg,
385 node,
386 collection_type,
387 maybe_element_type,
388 collection_self_addr,
389 &target_variables_to_use,
390 );
391
392 if let Some(temp_regs) = temp_registers {
394 for (temp_addr_reg, target_scalar_reg) in temp_regs {
395 let source_memory_location =
396 MemoryLocation::new_copy_over_whole_type_with_zero_offset(
397 temp_addr_reg,
398 );
399 self.emit_load_value_from_memory_source(
400 &target_scalar_reg,
401 &source_memory_location,
402 node,
403 "load scalar value from map iterator address",
404 );
405 }
406 }
407
408 patch_position
409 }
410 _ => {
411 let (temp_registers, target_variables_to_use) =
412 if primary_register.ty.basic_type.is_scalar() {
413 let temp_addr = self.temp_registers.allocate(
416 VmType::new_contained_in_register(pointer_type()),
417 "temp address for value",
418 );
419
420 if target_variables.len() == 2 {
421 (
422 Some(temp_addr.register().clone()),
423 vec![target_variables[0].clone(), temp_addr.register.clone()],
424 )
425 } else {
426 (
427 Some(temp_addr.register().clone()),
428 vec![temp_addr.register.clone()],
429 )
430 }
431 } else {
432 (None, target_variables.to_vec())
433 };
434
435 let patch_position = self.emit_iter_init_and_next_to_memory(
436 &target_iterator_header_reg,
437 node,
438 collection_type,
439 maybe_element_type,
440 collection_self_addr,
441 &target_variables_to_use,
442 );
443
444 if let Some(temp_regs) = temp_registers {
445 if collection_type == Collection::String {
446 self.builder.add_mov_reg(
447 primary_register,
448 &temp_regs,
449 node,
450 "copy from temporary to primary scalar register",
451 );
452 } else {
454 let source_memory_location =
455 MemoryLocation::new_copy_over_whole_type_with_zero_offset(temp_regs);
456 self.emit_load_value_from_memory_source(
457 primary_register,
458 &source_memory_location,
459 node,
460 "load primitive from element address",
461 );
462 }
463 }
464
465 patch_position
466 }
467 };
468
469 (iter_next_position, placeholder)
470 }
471
472 fn emit_iter_init_and_next_to_memory(
473 &mut self,
474 target_iterator_header_reg: &TypedRegister,
475 node: &Node,
476 collection_type: Collection,
477 maybe_element_type: Option<BasicTypeRef>,
478 collection_self_addr: &TypedRegister,
479 target_variables: &[TypedRegister],
480 ) -> PatchPosition {
481 let primary_register = if target_variables.len() == 2 {
482 &target_variables[1]
483 } else {
484 &target_variables[0]
485 };
486 let is_pair = target_variables.len() == 2;
487 match collection_type {
488 Collection::String => {
490 if maybe_element_type.is_none() {
491 error!(
492 ?collection_self_addr,
493 "can no start iterating with this strange vec collection"
494 );
495 }
496
497 self.builder.add_string_iter_init(
498 target_iterator_header_reg,
499 collection_self_addr,
500 maybe_element_type.unwrap().total_size,
501 node,
502 "vec iter init",
503 );
504
505 let is_pair = target_variables.len() == 2;
506
507 if is_pair {
508 self.builder.add_string_iter_next_pair_placeholder(
510 target_iterator_header_reg,
511 &target_variables[0],
512 &target_variables[1],
513 node,
514 "vec iter next pair",
515 )
516 } else {
517 self.builder.add_string_iter_next_placeholder(
519 target_iterator_header_reg,
520 primary_register,
521 node,
522 "vec iter next single",
523 )
524 }
525 }
526 Collection::Vec => {
527 if maybe_element_type.is_none() {
528 error!(
529 ?collection_self_addr,
530 "can no start iterating with this strange vec collection"
531 );
532 }
533
534 self.builder.add_vec_iter_init(
535 target_iterator_header_reg,
536 collection_self_addr,
537 node,
538 "vec iter init",
539 );
540
541 let is_pair = target_variables.len() == 2;
542
543 if is_pair {
544 self.builder.add_vec_iter_next_pair_placeholder(
546 target_iterator_header_reg,
547 &target_variables[0],
548 &target_variables[1],
549 node,
550 "vec iter next pair",
551 )
552 } else {
553 self.builder.add_vec_iter_next_placeholder(
555 target_iterator_header_reg,
556 primary_register,
557 node,
558 "vec iter next single",
559 )
560 }
561 }
562 Collection::Sparse => {
563 self.builder.add_sparse_iter_init(
564 target_iterator_header_reg,
565 collection_self_addr,
566 node,
567 "sparse init",
568 );
569
570 if is_pair {
571 self.builder.add_sparse_iter_next_pair_placeholder(
572 target_iterator_header_reg,
573 &target_variables[0],
574 &target_variables[1],
575 node,
576 "sparse next_pair",
577 )
578 } else {
579 self.builder.add_sparse_iter_next_placeholder(
580 target_iterator_header_reg,
581 primary_register,
582 node,
583 "sparse next_single",
584 )
585 }
586 }
587
588 Collection::Map => {
589 self.builder.add_map_iter_init(
590 target_iterator_header_reg,
591 collection_self_addr,
592 node,
593 "map init",
594 );
595
596 if target_variables.len() == 2 {
597 self.builder.add_map_iter_next_pair_placeholder(
598 target_iterator_header_reg,
599 &target_variables[0],
600 &target_variables[1],
601 node,
602 "map next_pair",
603 )
604 } else {
605 self.builder.add_map_iter_next_placeholder(
606 target_iterator_header_reg,
607 &target_variables[0],
608 node,
609 "map next_single",
610 )
611 }
612 }
613 Collection::Grid => todo!(),
614 _ => panic!("unknown collection"),
615 }
616 }
617
618 const fn check_if_transformer_sets_t_flag(
619 &mut self,
620 transformer: Transformer,
621 ) -> FlagStateKind {
622 match transformer {
623 Transformer::For => FlagStateKind::TFlagIsIndeterminate,
624 Transformer::While => FlagStateKind::TFlagIsTrueWhenSet,
625 Transformer::Filter => {
626 FlagStateKind::TFlagIsTrueWhenSet
628 }
629 Transformer::Find => {
630 FlagStateKind::TFlagIsTrueWhenClear
632 }
633 Transformer::Map => FlagStateKind::TFlagIsIndeterminate,
634 Transformer::Any => FlagStateKind::TFlagIsTrueWhenClear,
635 Transformer::All => FlagStateKind::TFlagIsTrueWhenSet,
636 Transformer::FilterMap => FlagStateKind::TFlagIsTrueWhenSet,
637 }
638 }
639
640 fn add_to_collection(
641 &mut self,
642 node: &Node,
643 collection: Collection,
644 mut_collection: &TypedRegister,
645 value: &TypedRegister,
646 ) {
647 match collection {
648 Collection::Vec => {
649 let target_element_addr_reg = self.temp_registers.allocate(
650 VmType::new_contained_in_register(u32_type()),
651 "address for the element entry",
652 );
653
654 self.builder.add_vec_push_addr(
655 target_element_addr_reg.register(),
656 mut_collection,
657 node,
658 "add_to_collection for transformer",
659 );
660
661 let vec_entry_destination =
662 Place::Memory(MemoryLocation::new_copy_over_whole_type_with_zero_offset(
663 target_element_addr_reg.register().clone(),
664 ));
665
666 if value.ty.basic_type.is_aggregate() {
668 self.emit_initialize_memory_for_any_type(
669 vec_entry_destination.grab_memory_location(),
670 node,
671 "initialize vec.push allocated space for transformer",
672 );
673 }
674
675 let source_destination = if value.ty.is_scalar() {
676 Place::Register(value.clone())
677 } else {
678 Place::Memory(MemoryLocation::new_copy_over_whole_type_with_zero_offset(
679 value.clone(),
680 ))
681 };
682
683 self.emit_store_value_to_memory_place(
684 &vec_entry_destination,
685 &source_destination,
686 node,
687 "store value in the new vec entry slot",
688 );
689 }
690 Collection::Sparse => panic!("not supported by sparse"),
691 Collection::Map => todo!(),
692 Collection::Grid => todo!(),
693 Collection::String => todo!(),
694 Collection::Range => todo!(),
695 }
696 }
697
698 fn transformer_add_to_collection(
699 &mut self,
700 in_value: &TypedRegister,
701 should_unwrap_value: bool,
702 collection_type: Collection,
703 mut_collection: &TypedRegister,
704 node: &Node,
705 ) {
706 let (register_to_be_inserted_in_collection, _maybe_temp) = if should_unwrap_value {
707 let tagged_union = in_value.underlying().optional_info().unwrap().clone();
708 let some_variant = tagged_union.get_variant_by_index(1);
709 let payload_vm_type = VmType::new_unknown_placement(some_variant.ty.clone());
710 let temp_reg = self
711 .temp_registers
712 .allocate(payload_vm_type, "transform add to collection");
713 let (_tag_offset, _tag_size, _payload_offset, _) =
714 in_value.underlying().unwrap_info().unwrap();
715
716 (temp_reg.register.clone(), Some(temp_reg))
730 } else {
731 (in_value.clone(), None)
732 };
733
734 self.add_to_collection(
735 node,
736 collection_type,
737 mut_collection,
738 ®ister_to_be_inserted_in_collection,
739 );
740 }
741
742 const fn check_if_transformer_can_skip_early(&self, transformer: Transformer) -> bool {
743 match transformer {
744 Transformer::For => false,
745 Transformer::Filter => false,
746 Transformer::Find => true,
747 Transformer::While => true,
748 Transformer::Map => false,
749 Transformer::Any => true,
750 Transformer::All => true,
751 Transformer::FilterMap => false,
752 }
753 }
754}