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