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