1use crate::stack::{Stack, drop_stack_value, pop, pop_sv, push};
23use crate::value::{Value, VariantData};
24use std::sync::Arc;
25
26unsafe fn drain_stack_to_base(mut stack: Stack, base: Stack) {
31 unsafe {
32 while stack > base {
33 let (rest, sv) = pop_sv(stack);
34 drop_stack_value(sv);
35 stack = rest;
36 }
37 }
38}
39
40unsafe fn call_with_value(base: Stack, value: Value, callable: &Value) -> Stack {
45 unsafe {
46 let stack = push(base, value);
47
48 match callable {
49 Value::Quotation { wrapper, .. } => {
50 let fn_ref: unsafe extern "C" fn(Stack) -> Stack = std::mem::transmute(*wrapper);
51 fn_ref(stack)
52 }
53 Value::Closure { fn_ptr, env } => {
54 let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
55 std::mem::transmute(*fn_ptr);
56 fn_ref(stack, env.as_ptr(), env.len())
57 }
58 _ => panic!("list operation: expected Quotation or Closure"),
59 }
60 }
61}
62
63#[unsafe(no_mangle)]
73pub unsafe extern "C" fn patch_seq_list_map(stack: Stack) -> Stack {
74 unsafe {
75 let (stack, callable) = pop(stack);
77
78 match &callable {
80 Value::Quotation { .. } | Value::Closure { .. } => {}
81 _ => panic!(
82 "list-map: expected Quotation or Closure, got {:?}",
83 callable
84 ),
85 }
86
87 let (stack, list_val) = pop(stack);
89
90 let variant_data = match list_val {
91 Value::Variant(v) => v,
92 _ => panic!("list-map: expected Variant (list), got {:?}", list_val),
93 };
94
95 let mut results = Vec::with_capacity(variant_data.fields.len());
97
98 for field in variant_data.fields.iter() {
99 let temp_base = crate::stack::alloc_stack();
101 let temp_stack = call_with_value(temp_base, field.clone(), &callable);
102
103 if temp_stack <= temp_base {
105 panic!("list-map: quotation consumed element without producing result");
106 }
107 let (remaining, result) = pop(temp_stack);
108 results.push(result);
109
110 if remaining > temp_base {
112 drain_stack_to_base(remaining, temp_base);
113 }
114 }
115
116 let new_variant = Value::Variant(Arc::new(VariantData::new(
118 variant_data.tag.clone(),
119 results,
120 )));
121
122 push(stack, new_variant)
123 }
124}
125
126#[unsafe(no_mangle)]
136pub unsafe extern "C" fn patch_seq_list_filter(stack: Stack) -> Stack {
137 unsafe {
138 let (stack, callable) = pop(stack);
140
141 match &callable {
143 Value::Quotation { .. } | Value::Closure { .. } => {}
144 _ => panic!(
145 "list-filter: expected Quotation or Closure, got {:?}",
146 callable
147 ),
148 }
149
150 let (stack, list_val) = pop(stack);
152
153 let variant_data = match list_val {
154 Value::Variant(v) => v,
155 _ => panic!("list-filter: expected Variant (list), got {:?}", list_val),
156 };
157
158 let mut results = Vec::new();
160
161 for field in variant_data.fields.iter() {
162 let temp_base = crate::stack::alloc_stack();
164 let temp_stack = call_with_value(temp_base, field.clone(), &callable);
165
166 if temp_stack <= temp_base {
168 panic!("list-filter: quotation consumed element without producing result");
169 }
170 let (remaining, result) = pop(temp_stack);
171
172 let keep = match result {
173 Value::Bool(b) => b,
174 _ => panic!("list-filter: quotation must return Bool, got {:?}", result),
175 };
176
177 if keep {
178 results.push(field.clone());
179 }
180
181 if remaining > temp_base {
183 drain_stack_to_base(remaining, temp_base);
184 }
185 }
186
187 let new_variant = Value::Variant(Arc::new(VariantData::new(
189 variant_data.tag.clone(),
190 results,
191 )));
192
193 push(stack, new_variant)
194 }
195}
196
197#[unsafe(no_mangle)]
207pub unsafe extern "C" fn patch_seq_list_fold(stack: Stack) -> Stack {
208 unsafe {
209 let (stack, callable) = pop(stack);
211
212 match &callable {
214 Value::Quotation { .. } | Value::Closure { .. } => {}
215 _ => panic!(
216 "list-fold: expected Quotation or Closure, got {:?}",
217 callable
218 ),
219 }
220
221 let (stack, init) = pop(stack);
223
224 let (stack, list_val) = pop(stack);
226
227 let variant_data = match list_val {
228 Value::Variant(v) => v,
229 _ => panic!("list-fold: expected Variant (list), got {:?}", list_val),
230 };
231
232 let mut acc = init;
234
235 for field in variant_data.fields.iter() {
236 let temp_base = crate::stack::alloc_stack();
238 let temp_stack = push(temp_base, acc);
239 let temp_stack = push(temp_stack, field.clone());
240
241 let temp_stack = match &callable {
242 Value::Quotation { wrapper, .. } => {
243 let fn_ref: unsafe extern "C" fn(Stack) -> Stack =
244 std::mem::transmute(*wrapper);
245 fn_ref(temp_stack)
246 }
247 Value::Closure { fn_ptr, env } => {
248 let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
249 std::mem::transmute(*fn_ptr);
250 fn_ref(temp_stack, env.as_ptr(), env.len())
251 }
252 _ => unreachable!(),
253 };
254
255 if temp_stack <= temp_base {
257 panic!("list-fold: quotation consumed inputs without producing result");
258 }
259 let (remaining, new_acc) = pop(temp_stack);
260 acc = new_acc;
261
262 if remaining > temp_base {
264 drain_stack_to_base(remaining, temp_base);
265 }
266 }
267
268 push(stack, acc)
269 }
270}
271
272#[unsafe(no_mangle)]
282pub unsafe extern "C" fn patch_seq_list_each(stack: Stack) -> Stack {
283 unsafe {
284 let (stack, callable) = pop(stack);
286
287 match &callable {
289 Value::Quotation { .. } | Value::Closure { .. } => {}
290 _ => panic!(
291 "list-each: expected Quotation or Closure, got {:?}",
292 callable
293 ),
294 }
295
296 let (stack, list_val) = pop(stack);
298
299 let variant_data = match list_val {
300 Value::Variant(v) => v,
301 _ => panic!("list-each: expected Variant (list), got {:?}", list_val),
302 };
303
304 for field in variant_data.fields.iter() {
306 let temp_base = crate::stack::alloc_stack();
307 let temp_stack = call_with_value(temp_base, field.clone(), &callable);
308 if temp_stack > temp_base {
310 drain_stack_to_base(temp_stack, temp_base);
311 }
312 }
313
314 stack
315 }
316}
317
318#[unsafe(no_mangle)]
328pub unsafe extern "C" fn patch_seq_list_length(stack: Stack) -> Stack {
329 unsafe { crate::variant_ops::patch_seq_variant_field_count(stack) }
330}
331
332#[unsafe(no_mangle)]
341pub unsafe extern "C" fn patch_seq_list_empty(stack: Stack) -> Stack {
342 unsafe {
343 let (stack, list_val) = pop(stack);
344
345 let is_empty = match list_val {
346 Value::Variant(v) => v.fields.is_empty(),
347 _ => panic!("list-empty?: expected Variant (list), got {:?}", list_val),
348 };
349
350 push(stack, Value::Bool(is_empty))
351 }
352}
353
354#[unsafe(no_mangle)]
363pub unsafe extern "C" fn patch_seq_list_make(stack: Stack) -> Stack {
364 unsafe {
365 let list = Value::Variant(Arc::new(VariantData::new(
366 crate::seqstring::global_string("List".to_string()),
367 vec![],
368 )));
369 push(stack, list)
370 }
371}
372
373#[unsafe(no_mangle)]
383pub unsafe extern "C" fn patch_seq_list_push(stack: Stack) -> Stack {
384 unsafe {
385 let (stack, value) = pop(stack);
386 let (stack, list_val) = pop(stack);
387
388 let variant_data = match list_val {
389 Value::Variant(v) => v,
390 _ => panic!("list.push: expected Variant (list), got {:?}", list_val),
391 };
392
393 let mut new_fields = Vec::with_capacity(variant_data.fields.len() + 1);
395 new_fields.extend(variant_data.fields.iter().cloned());
396 new_fields.push(value);
397
398 let new_list = Value::Variant(Arc::new(VariantData::new(
399 variant_data.tag.clone(),
400 new_fields,
401 )));
402
403 push(stack, new_list)
404 }
405}
406
407#[unsafe(no_mangle)]
417pub unsafe extern "C" fn patch_seq_list_get(stack: Stack) -> Stack {
418 unsafe {
419 let (stack, index_val) = pop(stack);
420 let (stack, list_val) = pop(stack);
421
422 let index = match index_val {
423 Value::Int(i) => i,
424 _ => panic!("list.get: expected Int (index), got {:?}", index_val),
425 };
426
427 let variant_data = match list_val {
428 Value::Variant(v) => v,
429 _ => panic!("list.get: expected Variant (list), got {:?}", list_val),
430 };
431
432 if index < 0 || index as usize >= variant_data.fields.len() {
433 let stack = push(stack, Value::Int(0)); push(stack, Value::Bool(false))
436 } else {
437 let value = variant_data.fields[index as usize].clone();
438 let stack = push(stack, value);
439 push(stack, Value::Bool(true))
440 }
441 }
442}
443
444#[unsafe(no_mangle)]
454pub unsafe extern "C" fn patch_seq_list_set(stack: Stack) -> Stack {
455 unsafe {
456 let (stack, value) = pop(stack);
457 let (stack, index_val) = pop(stack);
458 let (stack, list_val) = pop(stack);
459
460 let index = match index_val {
461 Value::Int(i) => i,
462 _ => panic!("list.set: expected Int (index), got {:?}", index_val),
463 };
464
465 let variant_data = match &list_val {
466 Value::Variant(v) => v,
467 _ => panic!("list.set: expected Variant (list), got {:?}", list_val),
468 };
469
470 if index < 0 || index as usize >= variant_data.fields.len() {
471 let stack = push(stack, list_val);
473 push(stack, Value::Bool(false))
474 } else {
475 let mut new_fields: Vec<Value> = variant_data.fields.to_vec();
477 new_fields[index as usize] = value;
478
479 let new_list = Value::Variant(Arc::new(VariantData::new(
480 variant_data.tag.clone(),
481 new_fields,
482 )));
483
484 let stack = push(stack, new_list);
485 push(stack, Value::Bool(true))
486 }
487 }
488}
489
490pub use patch_seq_list_each as list_each;
492pub use patch_seq_list_empty as list_empty;
493pub use patch_seq_list_filter as list_filter;
494pub use patch_seq_list_fold as list_fold;
495pub use patch_seq_list_get as list_get;
496pub use patch_seq_list_length as list_length;
497pub use patch_seq_list_make as list_make;
498pub use patch_seq_list_map as list_map;
499pub use patch_seq_list_push as list_push;
500pub use patch_seq_list_set as list_set;
501
502#[cfg(test)]
503mod tests {
504 use super::*;
505 use crate::seqstring::global_string;
506
507 unsafe extern "C" fn double_quot(stack: Stack) -> Stack {
509 unsafe {
510 let (stack, val) = pop(stack);
511 match val {
512 Value::Int(n) => push(stack, Value::Int(n * 2)),
513 _ => panic!("Expected Int"),
514 }
515 }
516 }
517
518 unsafe extern "C" fn is_positive_quot(stack: Stack) -> Stack {
520 unsafe {
521 let (stack, val) = pop(stack);
522 match val {
523 Value::Int(n) => push(stack, Value::Bool(n > 0)),
524 _ => panic!("Expected Int"),
525 }
526 }
527 }
528
529 unsafe extern "C" fn add_quot(stack: Stack) -> Stack {
531 unsafe {
532 let (stack, b) = pop(stack);
533 let (stack, a) = pop(stack);
534 match (a, b) {
535 (Value::Int(x), Value::Int(y)) => push(stack, Value::Int(x + y)),
536 _ => panic!("Expected two Ints"),
537 }
538 }
539 }
540
541 #[test]
542 fn test_list_map_double() {
543 unsafe {
544 let list = Value::Variant(Arc::new(VariantData::new(
546 global_string("List".to_string()),
547 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
548 )));
549
550 let stack = crate::stack::alloc_test_stack();
551 let stack = push(stack, list);
552 let fn_ptr = double_quot as usize;
553 let stack = push(
554 stack,
555 Value::Quotation {
556 wrapper: fn_ptr,
557 impl_: fn_ptr,
558 },
559 );
560 let stack = list_map(stack);
561
562 let (_stack, result) = pop(stack);
563 match result {
564 Value::Variant(v) => {
565 assert_eq!(v.fields.len(), 3);
566 assert_eq!(v.fields[0], Value::Int(2));
567 assert_eq!(v.fields[1], Value::Int(4));
568 assert_eq!(v.fields[2], Value::Int(6));
569 }
570 _ => panic!("Expected Variant"),
571 }
572 }
573 }
574
575 #[test]
576 fn test_list_filter_positive() {
577 unsafe {
578 let list = Value::Variant(Arc::new(VariantData::new(
580 global_string("List".to_string()),
581 vec![
582 Value::Int(-1),
583 Value::Int(2),
584 Value::Int(-3),
585 Value::Int(4),
586 Value::Int(0),
587 ],
588 )));
589
590 let stack = crate::stack::alloc_test_stack();
591 let stack = push(stack, list);
592 let fn_ptr = is_positive_quot as usize;
593 let stack = push(
594 stack,
595 Value::Quotation {
596 wrapper: fn_ptr,
597 impl_: fn_ptr,
598 },
599 );
600 let stack = list_filter(stack);
601
602 let (_stack, result) = pop(stack);
603 match result {
604 Value::Variant(v) => {
605 assert_eq!(v.fields.len(), 2);
606 assert_eq!(v.fields[0], Value::Int(2));
607 assert_eq!(v.fields[1], Value::Int(4));
608 }
609 _ => panic!("Expected Variant"),
610 }
611 }
612 }
613
614 #[test]
615 fn test_list_fold_sum() {
616 unsafe {
617 let list = Value::Variant(Arc::new(VariantData::new(
619 global_string("List".to_string()),
620 vec![
621 Value::Int(1),
622 Value::Int(2),
623 Value::Int(3),
624 Value::Int(4),
625 Value::Int(5),
626 ],
627 )));
628
629 let stack = crate::stack::alloc_test_stack();
630 let stack = push(stack, list);
631 let stack = push(stack, Value::Int(0)); let fn_ptr = add_quot as usize;
633 let stack = push(
634 stack,
635 Value::Quotation {
636 wrapper: fn_ptr,
637 impl_: fn_ptr,
638 },
639 );
640 let stack = list_fold(stack);
641
642 let (_stack, result) = pop(stack);
643 assert_eq!(result, Value::Int(15)); }
645 }
646
647 #[test]
648 fn test_list_fold_empty() {
649 unsafe {
650 let list = Value::Variant(Arc::new(VariantData::new(
652 global_string("List".to_string()),
653 vec![],
654 )));
655
656 let stack = crate::stack::alloc_test_stack();
657 let stack = push(stack, list);
658 let stack = push(stack, Value::Int(42)); let fn_ptr = add_quot as usize;
660 let stack = push(
661 stack,
662 Value::Quotation {
663 wrapper: fn_ptr,
664 impl_: fn_ptr,
665 },
666 );
667 let stack = list_fold(stack);
668
669 let (_stack, result) = pop(stack);
670 assert_eq!(result, Value::Int(42)); }
672 }
673
674 #[test]
675 fn test_list_length() {
676 unsafe {
677 let list = Value::Variant(Arc::new(VariantData::new(
678 global_string("List".to_string()),
679 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
680 )));
681
682 let stack = crate::stack::alloc_test_stack();
683 let stack = push(stack, list);
684 let stack = list_length(stack);
685
686 let (_stack, result) = pop(stack);
687 assert_eq!(result, Value::Int(3));
688 }
689 }
690
691 #[test]
692 fn test_list_empty_true() {
693 unsafe {
694 let list = Value::Variant(Arc::new(VariantData::new(
695 global_string("List".to_string()),
696 vec![],
697 )));
698
699 let stack = crate::stack::alloc_test_stack();
700 let stack = push(stack, list);
701 let stack = list_empty(stack);
702
703 let (_stack, result) = pop(stack);
704 assert_eq!(result, Value::Bool(true));
705 }
706 }
707
708 #[test]
709 fn test_list_empty_false() {
710 unsafe {
711 let list = Value::Variant(Arc::new(VariantData::new(
712 global_string("List".to_string()),
713 vec![Value::Int(1)],
714 )));
715
716 let stack = crate::stack::alloc_test_stack();
717 let stack = push(stack, list);
718 let stack = list_empty(stack);
719
720 let (_stack, result) = pop(stack);
721 assert_eq!(result, Value::Bool(false));
722 }
723 }
724
725 #[test]
726 fn test_list_map_empty() {
727 unsafe {
728 let list = Value::Variant(Arc::new(VariantData::new(
729 global_string("List".to_string()),
730 vec![],
731 )));
732
733 let stack = crate::stack::alloc_test_stack();
734 let stack = push(stack, list);
735 let fn_ptr = double_quot as usize;
736 let stack = push(
737 stack,
738 Value::Quotation {
739 wrapper: fn_ptr,
740 impl_: fn_ptr,
741 },
742 );
743 let stack = list_map(stack);
744
745 let (_stack, result) = pop(stack);
746 match result {
747 Value::Variant(v) => {
748 assert_eq!(v.fields.len(), 0);
749 }
750 _ => panic!("Expected Variant"),
751 }
752 }
753 }
754
755 #[test]
756 fn test_list_map_preserves_tag() {
757 unsafe {
758 let list = Value::Variant(Arc::new(VariantData::new(
760 global_string("CustomTag".to_string()),
761 vec![Value::Int(1), Value::Int(2)],
762 )));
763
764 let stack = crate::stack::alloc_test_stack();
765 let stack = push(stack, list);
766 let fn_ptr = double_quot as usize;
767 let stack = push(
768 stack,
769 Value::Quotation {
770 wrapper: fn_ptr,
771 impl_: fn_ptr,
772 },
773 );
774 let stack = list_map(stack);
775
776 let (_stack, result) = pop(stack);
777 match result {
778 Value::Variant(v) => {
779 assert_eq!(v.tag.as_str(), "CustomTag"); assert_eq!(v.fields[0], Value::Int(2));
781 assert_eq!(v.fields[1], Value::Int(4));
782 }
783 _ => panic!("Expected Variant"),
784 }
785 }
786 }
787
788 unsafe extern "C" fn add_captured_closure(
791 stack: Stack,
792 env: *const Value,
793 _env_len: usize,
794 ) -> Stack {
795 unsafe {
796 let (stack, val) = pop(stack);
797 let captured = &*env; match (val, captured) {
799 (Value::Int(n), Value::Int(c)) => push(stack, Value::Int(n + c)),
800 _ => panic!("Expected Int"),
801 }
802 }
803 }
804
805 #[test]
806 fn test_list_map_with_closure() {
807 unsafe {
808 let list = Value::Variant(Arc::new(VariantData::new(
810 global_string("List".to_string()),
811 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
812 )));
813
814 let env: std::sync::Arc<[Value]> =
816 std::sync::Arc::from(vec![Value::Int(10)].into_boxed_slice());
817 let closure = Value::Closure {
818 fn_ptr: add_captured_closure as usize,
819 env,
820 };
821
822 let stack = crate::stack::alloc_test_stack();
823 let stack = push(stack, list);
824 let stack = push(stack, closure);
825 let stack = list_map(stack);
826
827 let (_stack, result) = pop(stack);
828 match result {
829 Value::Variant(v) => {
830 assert_eq!(v.fields.len(), 3);
831 assert_eq!(v.fields[0], Value::Int(11)); assert_eq!(v.fields[1], Value::Int(12)); assert_eq!(v.fields[2], Value::Int(13)); }
835 _ => panic!("Expected Variant"),
836 }
837 }
838 }
839}