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(variant_data.tag, results)));
118
119 push(stack, new_variant)
120 }
121}
122
123#[unsafe(no_mangle)]
133pub unsafe extern "C" fn patch_seq_list_filter(stack: Stack) -> Stack {
134 unsafe {
135 let (stack, callable) = pop(stack);
137
138 match &callable {
140 Value::Quotation { .. } | Value::Closure { .. } => {}
141 _ => panic!(
142 "list-filter: expected Quotation or Closure, got {:?}",
143 callable
144 ),
145 }
146
147 let (stack, list_val) = pop(stack);
149
150 let variant_data = match list_val {
151 Value::Variant(v) => v,
152 _ => panic!("list-filter: expected Variant (list), got {:?}", list_val),
153 };
154
155 let mut results = Vec::new();
157
158 for field in variant_data.fields.iter() {
159 let temp_base = crate::stack::alloc_stack();
161 let temp_stack = call_with_value(temp_base, field.clone(), &callable);
162
163 if temp_stack <= temp_base {
165 panic!("list-filter: quotation consumed element without producing result");
166 }
167 let (remaining, result) = pop(temp_stack);
168
169 let keep = match result {
170 Value::Int(n) => n != 0,
171 _ => panic!("list-filter: quotation must return Int, got {:?}", result),
172 };
173
174 if keep {
175 results.push(field.clone());
176 }
177
178 if remaining > temp_base {
180 drain_stack_to_base(remaining, temp_base);
181 }
182 }
183
184 let new_variant = Value::Variant(Arc::new(VariantData::new(variant_data.tag, results)));
186
187 push(stack, new_variant)
188 }
189}
190
191#[unsafe(no_mangle)]
201pub unsafe extern "C" fn patch_seq_list_fold(stack: Stack) -> Stack {
202 unsafe {
203 let (stack, callable) = pop(stack);
205
206 match &callable {
208 Value::Quotation { .. } | Value::Closure { .. } => {}
209 _ => panic!(
210 "list-fold: expected Quotation or Closure, got {:?}",
211 callable
212 ),
213 }
214
215 let (stack, init) = pop(stack);
217
218 let (stack, list_val) = pop(stack);
220
221 let variant_data = match list_val {
222 Value::Variant(v) => v,
223 _ => panic!("list-fold: expected Variant (list), got {:?}", list_val),
224 };
225
226 let mut acc = init;
228
229 for field in variant_data.fields.iter() {
230 let temp_base = crate::stack::alloc_stack();
232 let temp_stack = push(temp_base, acc);
233 let temp_stack = push(temp_stack, field.clone());
234
235 let temp_stack = match &callable {
236 Value::Quotation { wrapper, .. } => {
237 let fn_ref: unsafe extern "C" fn(Stack) -> Stack =
238 std::mem::transmute(*wrapper);
239 fn_ref(temp_stack)
240 }
241 Value::Closure { fn_ptr, env } => {
242 let fn_ref: unsafe extern "C" fn(Stack, *const Value, usize) -> Stack =
243 std::mem::transmute(*fn_ptr);
244 fn_ref(temp_stack, env.as_ptr(), env.len())
245 }
246 _ => unreachable!(),
247 };
248
249 if temp_stack <= temp_base {
251 panic!("list-fold: quotation consumed inputs without producing result");
252 }
253 let (remaining, new_acc) = pop(temp_stack);
254 acc = new_acc;
255
256 if remaining > temp_base {
258 drain_stack_to_base(remaining, temp_base);
259 }
260 }
261
262 push(stack, acc)
263 }
264}
265
266#[unsafe(no_mangle)]
276pub unsafe extern "C" fn patch_seq_list_each(stack: Stack) -> Stack {
277 unsafe {
278 let (stack, callable) = pop(stack);
280
281 match &callable {
283 Value::Quotation { .. } | Value::Closure { .. } => {}
284 _ => panic!(
285 "list-each: expected Quotation or Closure, got {:?}",
286 callable
287 ),
288 }
289
290 let (stack, list_val) = pop(stack);
292
293 let variant_data = match list_val {
294 Value::Variant(v) => v,
295 _ => panic!("list-each: expected Variant (list), got {:?}", list_val),
296 };
297
298 for field in variant_data.fields.iter() {
300 let temp_base = crate::stack::alloc_stack();
301 let temp_stack = call_with_value(temp_base, field.clone(), &callable);
302 if temp_stack > temp_base {
304 drain_stack_to_base(temp_stack, temp_base);
305 }
306 }
307
308 stack
309 }
310}
311
312#[unsafe(no_mangle)]
322pub unsafe extern "C" fn patch_seq_list_length(stack: Stack) -> Stack {
323 unsafe { crate::variant_ops::patch_seq_variant_field_count(stack) }
324}
325
326#[unsafe(no_mangle)]
335pub unsafe extern "C" fn patch_seq_list_empty(stack: Stack) -> Stack {
336 unsafe {
337 let (stack, list_val) = pop(stack);
338
339 let is_empty = match list_val {
340 Value::Variant(v) => {
341 if v.fields.is_empty() {
342 1i64
343 } else {
344 0i64
345 }
346 }
347 _ => panic!("list-empty?: expected Variant (list), got {:?}", list_val),
348 };
349
350 push(stack, Value::Int(is_empty))
351 }
352}
353
354pub use patch_seq_list_each as list_each;
356pub use patch_seq_list_empty as list_empty;
357pub use patch_seq_list_filter as list_filter;
358pub use patch_seq_list_fold as list_fold;
359pub use patch_seq_list_length as list_length;
360pub use patch_seq_list_map as list_map;
361
362#[cfg(test)]
363mod tests {
364 use super::*;
365
366 unsafe extern "C" fn double_quot(stack: Stack) -> Stack {
368 unsafe {
369 let (stack, val) = pop(stack);
370 match val {
371 Value::Int(n) => push(stack, Value::Int(n * 2)),
372 _ => panic!("Expected Int"),
373 }
374 }
375 }
376
377 unsafe extern "C" fn is_positive_quot(stack: Stack) -> Stack {
379 unsafe {
380 let (stack, val) = pop(stack);
381 match val {
382 Value::Int(n) => push(stack, Value::Int(if n > 0 { 1 } else { 0 })),
383 _ => panic!("Expected Int"),
384 }
385 }
386 }
387
388 unsafe extern "C" fn add_quot(stack: Stack) -> Stack {
390 unsafe {
391 let (stack, b) = pop(stack);
392 let (stack, a) = pop(stack);
393 match (a, b) {
394 (Value::Int(x), Value::Int(y)) => push(stack, Value::Int(x + y)),
395 _ => panic!("Expected two Ints"),
396 }
397 }
398 }
399
400 #[test]
401 fn test_list_map_double() {
402 unsafe {
403 let list = Value::Variant(Arc::new(VariantData::new(
405 0,
406 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
407 )));
408
409 let stack = crate::stack::alloc_test_stack();
410 let stack = push(stack, list);
411 let fn_ptr = double_quot as usize;
412 let stack = push(
413 stack,
414 Value::Quotation {
415 wrapper: fn_ptr,
416 impl_: fn_ptr,
417 },
418 );
419 let stack = list_map(stack);
420
421 let (_stack, result) = pop(stack);
422 match result {
423 Value::Variant(v) => {
424 assert_eq!(v.fields.len(), 3);
425 assert_eq!(v.fields[0], Value::Int(2));
426 assert_eq!(v.fields[1], Value::Int(4));
427 assert_eq!(v.fields[2], Value::Int(6));
428 }
429 _ => panic!("Expected Variant"),
430 }
431 }
432 }
433
434 #[test]
435 fn test_list_filter_positive() {
436 unsafe {
437 let list = Value::Variant(Arc::new(VariantData::new(
439 0,
440 vec![
441 Value::Int(-1),
442 Value::Int(2),
443 Value::Int(-3),
444 Value::Int(4),
445 Value::Int(0),
446 ],
447 )));
448
449 let stack = crate::stack::alloc_test_stack();
450 let stack = push(stack, list);
451 let fn_ptr = is_positive_quot as usize;
452 let stack = push(
453 stack,
454 Value::Quotation {
455 wrapper: fn_ptr,
456 impl_: fn_ptr,
457 },
458 );
459 let stack = list_filter(stack);
460
461 let (_stack, result) = pop(stack);
462 match result {
463 Value::Variant(v) => {
464 assert_eq!(v.fields.len(), 2);
465 assert_eq!(v.fields[0], Value::Int(2));
466 assert_eq!(v.fields[1], Value::Int(4));
467 }
468 _ => panic!("Expected Variant"),
469 }
470 }
471 }
472
473 #[test]
474 fn test_list_fold_sum() {
475 unsafe {
476 let list = Value::Variant(Arc::new(VariantData::new(
478 0,
479 vec![
480 Value::Int(1),
481 Value::Int(2),
482 Value::Int(3),
483 Value::Int(4),
484 Value::Int(5),
485 ],
486 )));
487
488 let stack = crate::stack::alloc_test_stack();
489 let stack = push(stack, list);
490 let stack = push(stack, Value::Int(0)); let fn_ptr = add_quot as usize;
492 let stack = push(
493 stack,
494 Value::Quotation {
495 wrapper: fn_ptr,
496 impl_: fn_ptr,
497 },
498 );
499 let stack = list_fold(stack);
500
501 let (_stack, result) = pop(stack);
502 assert_eq!(result, Value::Int(15)); }
504 }
505
506 #[test]
507 fn test_list_fold_empty() {
508 unsafe {
509 let list = Value::Variant(Arc::new(VariantData::new(0, vec![])));
511
512 let stack = crate::stack::alloc_test_stack();
513 let stack = push(stack, list);
514 let stack = push(stack, Value::Int(42)); let fn_ptr = add_quot as usize;
516 let stack = push(
517 stack,
518 Value::Quotation {
519 wrapper: fn_ptr,
520 impl_: fn_ptr,
521 },
522 );
523 let stack = list_fold(stack);
524
525 let (_stack, result) = pop(stack);
526 assert_eq!(result, Value::Int(42)); }
528 }
529
530 #[test]
531 fn test_list_length() {
532 unsafe {
533 let list = Value::Variant(Arc::new(VariantData::new(
534 0,
535 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
536 )));
537
538 let stack = crate::stack::alloc_test_stack();
539 let stack = push(stack, list);
540 let stack = list_length(stack);
541
542 let (_stack, result) = pop(stack);
543 assert_eq!(result, Value::Int(3));
544 }
545 }
546
547 #[test]
548 fn test_list_empty_true() {
549 unsafe {
550 let list = Value::Variant(Arc::new(VariantData::new(0, vec![])));
551
552 let stack = crate::stack::alloc_test_stack();
553 let stack = push(stack, list);
554 let stack = list_empty(stack);
555
556 let (_stack, result) = pop(stack);
557 assert_eq!(result, Value::Int(1));
558 }
559 }
560
561 #[test]
562 fn test_list_empty_false() {
563 unsafe {
564 let list = Value::Variant(Arc::new(VariantData::new(0, vec![Value::Int(1)])));
565
566 let stack = crate::stack::alloc_test_stack();
567 let stack = push(stack, list);
568 let stack = list_empty(stack);
569
570 let (_stack, result) = pop(stack);
571 assert_eq!(result, Value::Int(0));
572 }
573 }
574
575 #[test]
576 fn test_list_map_empty() {
577 unsafe {
578 let list = Value::Variant(Arc::new(VariantData::new(0, vec![])));
579
580 let stack = crate::stack::alloc_test_stack();
581 let stack = push(stack, list);
582 let fn_ptr = double_quot as usize;
583 let stack = push(
584 stack,
585 Value::Quotation {
586 wrapper: fn_ptr,
587 impl_: fn_ptr,
588 },
589 );
590 let stack = list_map(stack);
591
592 let (_stack, result) = pop(stack);
593 match result {
594 Value::Variant(v) => {
595 assert_eq!(v.fields.len(), 0);
596 }
597 _ => panic!("Expected Variant"),
598 }
599 }
600 }
601
602 #[test]
603 fn test_list_map_preserves_tag() {
604 unsafe {
605 let list = Value::Variant(Arc::new(VariantData::new(
607 42,
608 vec![Value::Int(1), Value::Int(2)],
609 )));
610
611 let stack = crate::stack::alloc_test_stack();
612 let stack = push(stack, list);
613 let fn_ptr = double_quot as usize;
614 let stack = push(
615 stack,
616 Value::Quotation {
617 wrapper: fn_ptr,
618 impl_: fn_ptr,
619 },
620 );
621 let stack = list_map(stack);
622
623 let (_stack, result) = pop(stack);
624 match result {
625 Value::Variant(v) => {
626 assert_eq!(v.tag, 42); assert_eq!(v.fields[0], Value::Int(2));
628 assert_eq!(v.fields[1], Value::Int(4));
629 }
630 _ => panic!("Expected Variant"),
631 }
632 }
633 }
634
635 unsafe extern "C" fn add_captured_closure(
638 stack: Stack,
639 env: *const Value,
640 _env_len: usize,
641 ) -> Stack {
642 unsafe {
643 let (stack, val) = pop(stack);
644 let captured = &*env; match (val, captured) {
646 (Value::Int(n), Value::Int(c)) => push(stack, Value::Int(n + c)),
647 _ => panic!("Expected Int"),
648 }
649 }
650 }
651
652 #[test]
653 fn test_list_map_with_closure() {
654 unsafe {
655 let list = Value::Variant(Arc::new(VariantData::new(
657 0,
658 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
659 )));
660
661 let env: std::sync::Arc<[Value]> =
663 std::sync::Arc::from(vec![Value::Int(10)].into_boxed_slice());
664 let closure = Value::Closure {
665 fn_ptr: add_captured_closure as usize,
666 env,
667 };
668
669 let stack = crate::stack::alloc_test_stack();
670 let stack = push(stack, list);
671 let stack = push(stack, closure);
672 let stack = list_map(stack);
673
674 let (_stack, result) = pop(stack);
675 match result {
676 Value::Variant(v) => {
677 assert_eq!(v.fields.len(), 3);
678 assert_eq!(v.fields[0], Value::Int(11)); assert_eq!(v.fields[1], Value::Int(12)); assert_eq!(v.fields[2], Value::Int(13)); }
682 _ => panic!("Expected Variant"),
683 }
684 }
685 }
686}