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::Bool(b) => b,
171 _ => panic!("list-filter: quotation must return Bool, 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) => v.fields.is_empty(),
341 _ => panic!("list-empty?: expected Variant (list), got {:?}", list_val),
342 };
343
344 push(stack, Value::Bool(is_empty))
345 }
346}
347
348pub use patch_seq_list_each as list_each;
350pub use patch_seq_list_empty as list_empty;
351pub use patch_seq_list_filter as list_filter;
352pub use patch_seq_list_fold as list_fold;
353pub use patch_seq_list_length as list_length;
354pub use patch_seq_list_map as list_map;
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 unsafe extern "C" fn double_quot(stack: Stack) -> Stack {
362 unsafe {
363 let (stack, val) = pop(stack);
364 match val {
365 Value::Int(n) => push(stack, Value::Int(n * 2)),
366 _ => panic!("Expected Int"),
367 }
368 }
369 }
370
371 unsafe extern "C" fn is_positive_quot(stack: Stack) -> Stack {
373 unsafe {
374 let (stack, val) = pop(stack);
375 match val {
376 Value::Int(n) => push(stack, Value::Bool(n > 0)),
377 _ => panic!("Expected Int"),
378 }
379 }
380 }
381
382 unsafe extern "C" fn add_quot(stack: Stack) -> Stack {
384 unsafe {
385 let (stack, b) = pop(stack);
386 let (stack, a) = pop(stack);
387 match (a, b) {
388 (Value::Int(x), Value::Int(y)) => push(stack, Value::Int(x + y)),
389 _ => panic!("Expected two Ints"),
390 }
391 }
392 }
393
394 #[test]
395 fn test_list_map_double() {
396 unsafe {
397 let list = Value::Variant(Arc::new(VariantData::new(
399 0,
400 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
401 )));
402
403 let stack = crate::stack::alloc_test_stack();
404 let stack = push(stack, list);
405 let fn_ptr = double_quot as usize;
406 let stack = push(
407 stack,
408 Value::Quotation {
409 wrapper: fn_ptr,
410 impl_: fn_ptr,
411 },
412 );
413 let stack = list_map(stack);
414
415 let (_stack, result) = pop(stack);
416 match result {
417 Value::Variant(v) => {
418 assert_eq!(v.fields.len(), 3);
419 assert_eq!(v.fields[0], Value::Int(2));
420 assert_eq!(v.fields[1], Value::Int(4));
421 assert_eq!(v.fields[2], Value::Int(6));
422 }
423 _ => panic!("Expected Variant"),
424 }
425 }
426 }
427
428 #[test]
429 fn test_list_filter_positive() {
430 unsafe {
431 let list = Value::Variant(Arc::new(VariantData::new(
433 0,
434 vec![
435 Value::Int(-1),
436 Value::Int(2),
437 Value::Int(-3),
438 Value::Int(4),
439 Value::Int(0),
440 ],
441 )));
442
443 let stack = crate::stack::alloc_test_stack();
444 let stack = push(stack, list);
445 let fn_ptr = is_positive_quot as usize;
446 let stack = push(
447 stack,
448 Value::Quotation {
449 wrapper: fn_ptr,
450 impl_: fn_ptr,
451 },
452 );
453 let stack = list_filter(stack);
454
455 let (_stack, result) = pop(stack);
456 match result {
457 Value::Variant(v) => {
458 assert_eq!(v.fields.len(), 2);
459 assert_eq!(v.fields[0], Value::Int(2));
460 assert_eq!(v.fields[1], Value::Int(4));
461 }
462 _ => panic!("Expected Variant"),
463 }
464 }
465 }
466
467 #[test]
468 fn test_list_fold_sum() {
469 unsafe {
470 let list = Value::Variant(Arc::new(VariantData::new(
472 0,
473 vec![
474 Value::Int(1),
475 Value::Int(2),
476 Value::Int(3),
477 Value::Int(4),
478 Value::Int(5),
479 ],
480 )));
481
482 let stack = crate::stack::alloc_test_stack();
483 let stack = push(stack, list);
484 let stack = push(stack, Value::Int(0)); let fn_ptr = add_quot as usize;
486 let stack = push(
487 stack,
488 Value::Quotation {
489 wrapper: fn_ptr,
490 impl_: fn_ptr,
491 },
492 );
493 let stack = list_fold(stack);
494
495 let (_stack, result) = pop(stack);
496 assert_eq!(result, Value::Int(15)); }
498 }
499
500 #[test]
501 fn test_list_fold_empty() {
502 unsafe {
503 let list = Value::Variant(Arc::new(VariantData::new(0, vec![])));
505
506 let stack = crate::stack::alloc_test_stack();
507 let stack = push(stack, list);
508 let stack = push(stack, Value::Int(42)); let fn_ptr = add_quot as usize;
510 let stack = push(
511 stack,
512 Value::Quotation {
513 wrapper: fn_ptr,
514 impl_: fn_ptr,
515 },
516 );
517 let stack = list_fold(stack);
518
519 let (_stack, result) = pop(stack);
520 assert_eq!(result, Value::Int(42)); }
522 }
523
524 #[test]
525 fn test_list_length() {
526 unsafe {
527 let list = Value::Variant(Arc::new(VariantData::new(
528 0,
529 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
530 )));
531
532 let stack = crate::stack::alloc_test_stack();
533 let stack = push(stack, list);
534 let stack = list_length(stack);
535
536 let (_stack, result) = pop(stack);
537 assert_eq!(result, Value::Int(3));
538 }
539 }
540
541 #[test]
542 fn test_list_empty_true() {
543 unsafe {
544 let list = Value::Variant(Arc::new(VariantData::new(0, vec![])));
545
546 let stack = crate::stack::alloc_test_stack();
547 let stack = push(stack, list);
548 let stack = list_empty(stack);
549
550 let (_stack, result) = pop(stack);
551 assert_eq!(result, Value::Bool(true));
552 }
553 }
554
555 #[test]
556 fn test_list_empty_false() {
557 unsafe {
558 let list = Value::Variant(Arc::new(VariantData::new(0, vec![Value::Int(1)])));
559
560 let stack = crate::stack::alloc_test_stack();
561 let stack = push(stack, list);
562 let stack = list_empty(stack);
563
564 let (_stack, result) = pop(stack);
565 assert_eq!(result, Value::Bool(false));
566 }
567 }
568
569 #[test]
570 fn test_list_map_empty() {
571 unsafe {
572 let list = Value::Variant(Arc::new(VariantData::new(0, vec![])));
573
574 let stack = crate::stack::alloc_test_stack();
575 let stack = push(stack, list);
576 let fn_ptr = double_quot as usize;
577 let stack = push(
578 stack,
579 Value::Quotation {
580 wrapper: fn_ptr,
581 impl_: fn_ptr,
582 },
583 );
584 let stack = list_map(stack);
585
586 let (_stack, result) = pop(stack);
587 match result {
588 Value::Variant(v) => {
589 assert_eq!(v.fields.len(), 0);
590 }
591 _ => panic!("Expected Variant"),
592 }
593 }
594 }
595
596 #[test]
597 fn test_list_map_preserves_tag() {
598 unsafe {
599 let list = Value::Variant(Arc::new(VariantData::new(
601 42,
602 vec![Value::Int(1), Value::Int(2)],
603 )));
604
605 let stack = crate::stack::alloc_test_stack();
606 let stack = push(stack, list);
607 let fn_ptr = double_quot as usize;
608 let stack = push(
609 stack,
610 Value::Quotation {
611 wrapper: fn_ptr,
612 impl_: fn_ptr,
613 },
614 );
615 let stack = list_map(stack);
616
617 let (_stack, result) = pop(stack);
618 match result {
619 Value::Variant(v) => {
620 assert_eq!(v.tag, 42); assert_eq!(v.fields[0], Value::Int(2));
622 assert_eq!(v.fields[1], Value::Int(4));
623 }
624 _ => panic!("Expected Variant"),
625 }
626 }
627 }
628
629 unsafe extern "C" fn add_captured_closure(
632 stack: Stack,
633 env: *const Value,
634 _env_len: usize,
635 ) -> Stack {
636 unsafe {
637 let (stack, val) = pop(stack);
638 let captured = &*env; match (val, captured) {
640 (Value::Int(n), Value::Int(c)) => push(stack, Value::Int(n + c)),
641 _ => panic!("Expected Int"),
642 }
643 }
644 }
645
646 #[test]
647 fn test_list_map_with_closure() {
648 unsafe {
649 let list = Value::Variant(Arc::new(VariantData::new(
651 0,
652 vec![Value::Int(1), Value::Int(2), Value::Int(3)],
653 )));
654
655 let env: std::sync::Arc<[Value]> =
657 std::sync::Arc::from(vec![Value::Int(10)].into_boxed_slice());
658 let closure = Value::Closure {
659 fn_ptr: add_captured_closure as usize,
660 env,
661 };
662
663 let stack = crate::stack::alloc_test_stack();
664 let stack = push(stack, list);
665 let stack = push(stack, closure);
666 let stack = list_map(stack);
667
668 let (_stack, result) = pop(stack);
669 match result {
670 Value::Variant(v) => {
671 assert_eq!(v.fields.len(), 3);
672 assert_eq!(v.fields[0], Value::Int(11)); assert_eq!(v.fields[1], Value::Int(12)); assert_eq!(v.fields[2], Value::Int(13)); }
676 _ => panic!("Expected Variant"),
677 }
678 }
679 }
680}