Skip to main content

seq_runtime/
variant_ops.rs

1//! Variant field access operations for Seq
2//!
3//! Provides runtime functions for accessing variant fields, tags, and metadata.
4//! These are used to work with composite data created by operations like string-split.
5
6use crate::stack::{Stack, peek_heap_mut_second, pop, push};
7use crate::value::Value;
8use std::sync::Arc;
9
10/// Get the number of fields in a variant
11///
12/// Stack effect: ( Variant -- Int )
13///
14/// # Safety
15/// Stack must have a Variant on top
16#[unsafe(no_mangle)]
17pub unsafe extern "C" fn patch_seq_variant_field_count(stack: Stack) -> Stack {
18    unsafe {
19        let (stack, value) = pop(stack);
20
21        match value {
22            Value::Variant(variant_data) => {
23                let count = variant_data.fields.len() as i64;
24                push(stack, Value::Int(count))
25            }
26            _ => panic!("variant-field-count: expected Variant, got {:?}", value),
27        }
28    }
29}
30
31/// Get the tag of a variant
32///
33/// Stack effect: ( Variant -- Symbol )
34///
35/// # Safety
36/// Stack must have a Variant on top
37#[unsafe(no_mangle)]
38pub unsafe extern "C" fn patch_seq_variant_tag(stack: Stack) -> Stack {
39    unsafe {
40        let (stack, value) = pop(stack);
41
42        match value {
43            Value::Variant(variant_data) => {
44                // Return the tag as a Symbol
45                push(stack, Value::Symbol(variant_data.tag.clone()))
46            }
47            _ => panic!("variant-tag: expected Variant, got {:?}", value),
48        }
49    }
50}
51
52/// Compare a symbol tag with a C string literal
53///
54/// Used by pattern matching codegen to dispatch on variant tags.
55/// The stack should have a Symbol on top (typically from variant-tag).
56/// Compares with the provided C string and pushes Bool result.
57///
58/// Stack effect: ( Symbol -- Bool )
59///
60/// # Safety
61/// - Stack must have a Symbol on top
62/// - c_str must be a valid null-terminated C string
63#[unsafe(no_mangle)]
64pub unsafe extern "C" fn patch_seq_symbol_eq_cstr(stack: Stack, c_str: *const i8) -> Stack {
65    use std::ffi::CStr;
66
67    unsafe {
68        let (stack, value) = pop(stack);
69        let symbol_str = match value {
70            Value::Symbol(s) => s,
71            _ => panic!("symbol_eq_cstr: expected Symbol, got {:?}", value),
72        };
73
74        let expected = CStr::from_ptr(c_str)
75            .to_str()
76            .expect("Invalid UTF-8 in variant name");
77
78        let is_equal = symbol_str.as_str() == expected;
79        push(stack, Value::Bool(is_equal))
80    }
81}
82
83/// Get a field from a variant at the given index
84///
85/// Stack effect: ( Variant Int -- Value )
86///
87/// Returns a clone of the field value at the specified index.
88/// Panics if index is out of bounds (this is a programming bug, not recoverable).
89/// Use variant.field-count to check bounds first if needed.
90///
91/// # Safety
92/// Stack must have a Variant and Int on top
93#[unsafe(no_mangle)]
94pub unsafe extern "C" fn patch_seq_variant_field_at(stack: Stack) -> Stack {
95    unsafe {
96        let (stack, index_val) = pop(stack);
97        let index = match index_val {
98            Value::Int(i) => i,
99            _ => panic!(
100                "variant-field-at: expected Int (index), got {:?}",
101                index_val
102            ),
103        };
104
105        if index < 0 {
106            panic!("variant-field-at: index cannot be negative: {}", index);
107        }
108
109        let (stack, variant_val) = pop(stack);
110
111        match variant_val {
112            Value::Variant(variant_data) => {
113                let idx = index as usize;
114                if idx >= variant_data.fields.len() {
115                    panic!(
116                        "variant-field-at: index {} out of bounds (variant has {} fields)",
117                        index,
118                        variant_data.fields.len()
119                    );
120                }
121
122                // Clone the field value and push it
123                let field = variant_data.fields[idx].clone();
124                push(stack, field)
125            }
126            _ => panic!("variant-field-at: expected Variant, got {:?}", variant_val),
127        }
128    }
129}
130
131// ============================================================================
132// Type-safe variant constructors with fixed arity
133// Now accept Symbol as tag for dynamic variant construction (SON support)
134// ============================================================================
135
136/// Create a variant with 0 fields (just a tag)
137///
138/// Stack effect: ( Symbol -- Variant )
139///
140/// # Safety
141/// Stack must have at least one Symbol (the tag) on top
142#[unsafe(no_mangle)]
143pub unsafe extern "C" fn patch_seq_make_variant_0(stack: Stack) -> Stack {
144    use crate::value::VariantData;
145
146    unsafe {
147        let (stack, tag_val) = pop(stack);
148        let tag = match tag_val {
149            Value::Symbol(s) => s,
150            _ => panic!("make-variant-0: expected Symbol (tag), got {:?}", tag_val),
151        };
152
153        let variant = Value::Variant(Arc::new(VariantData::new(tag, vec![])));
154        push(stack, variant)
155    }
156}
157
158/// Create a variant with 1 field
159///
160/// Stack effect: ( field1 Symbol -- Variant )
161///
162/// # Safety
163/// Stack must have field1 and Symbol tag on top
164#[unsafe(no_mangle)]
165pub unsafe extern "C" fn patch_seq_make_variant_1(stack: Stack) -> Stack {
166    use crate::value::VariantData;
167
168    unsafe {
169        let (stack, tag_val) = pop(stack);
170        let tag = match tag_val {
171            Value::Symbol(s) => s,
172            _ => panic!("make-variant-1: expected Symbol (tag), got {:?}", tag_val),
173        };
174
175        let (stack, field1) = pop(stack);
176        let variant = Value::Variant(Arc::new(VariantData::new(tag, vec![field1])));
177        push(stack, variant)
178    }
179}
180
181/// Create a variant with 2 fields
182///
183/// Stack effect: ( field1 field2 Symbol -- Variant )
184///
185/// # Safety
186/// Stack must have field1, field2, and Symbol tag on top
187#[unsafe(no_mangle)]
188pub unsafe extern "C" fn patch_seq_make_variant_2(stack: Stack) -> Stack {
189    use crate::value::VariantData;
190
191    unsafe {
192        let (stack, tag_val) = pop(stack);
193        let tag = match tag_val {
194            Value::Symbol(s) => s,
195            _ => panic!("make-variant-2: expected Symbol (tag), got {:?}", tag_val),
196        };
197
198        let (stack, field2) = pop(stack);
199        let (stack, field1) = pop(stack);
200        let variant = Value::Variant(Arc::new(VariantData::new(tag, vec![field1, field2])));
201        push(stack, variant)
202    }
203}
204
205/// Create a variant with 3 fields
206///
207/// Stack effect: ( field1 field2 field3 Symbol -- Variant )
208///
209/// # Safety
210/// Stack must have field1, field2, field3, and Symbol tag on top
211#[unsafe(no_mangle)]
212pub unsafe extern "C" fn patch_seq_make_variant_3(stack: Stack) -> Stack {
213    use crate::value::VariantData;
214
215    unsafe {
216        let (stack, tag_val) = pop(stack);
217        let tag = match tag_val {
218            Value::Symbol(s) => s,
219            _ => panic!("make-variant-3: expected Symbol (tag), got {:?}", tag_val),
220        };
221
222        let (stack, field3) = pop(stack);
223        let (stack, field2) = pop(stack);
224        let (stack, field1) = pop(stack);
225        let variant = Value::Variant(Arc::new(VariantData::new(
226            tag,
227            vec![field1, field2, field3],
228        )));
229        push(stack, variant)
230    }
231}
232
233/// Create a variant with 4 fields
234///
235/// Stack effect: ( field1 field2 field3 field4 Symbol -- Variant )
236///
237/// # Safety
238/// Stack must have field1, field2, field3, field4, and Symbol tag on top
239#[unsafe(no_mangle)]
240pub unsafe extern "C" fn patch_seq_make_variant_4(stack: Stack) -> Stack {
241    use crate::value::VariantData;
242
243    unsafe {
244        let (stack, tag_val) = pop(stack);
245        let tag = match tag_val {
246            Value::Symbol(s) => s,
247            _ => panic!("make-variant-4: expected Symbol (tag), got {:?}", tag_val),
248        };
249
250        let (stack, field4) = pop(stack);
251        let (stack, field3) = pop(stack);
252        let (stack, field2) = pop(stack);
253        let (stack, field1) = pop(stack);
254        let variant = Value::Variant(Arc::new(VariantData::new(
255            tag,
256            vec![field1, field2, field3, field4],
257        )));
258        push(stack, variant)
259    }
260}
261
262/// Create a variant with 5 fields
263///
264/// Stack effect: ( field1 field2 field3 field4 field5 Symbol -- Variant )
265///
266/// # Safety
267/// Stack must have 5 fields and Symbol tag on top
268#[unsafe(no_mangle)]
269pub unsafe extern "C" fn patch_seq_make_variant_5(stack: Stack) -> Stack {
270    use crate::value::VariantData;
271
272    unsafe {
273        let (stack, tag_val) = pop(stack);
274        let tag = match tag_val {
275            Value::Symbol(s) => s,
276            _ => panic!("make-variant-5: expected Symbol (tag), got {:?}", tag_val),
277        };
278
279        let (stack, field5) = pop(stack);
280        let (stack, field4) = pop(stack);
281        let (stack, field3) = pop(stack);
282        let (stack, field2) = pop(stack);
283        let (stack, field1) = pop(stack);
284        let variant = Value::Variant(Arc::new(VariantData::new(
285            tag,
286            vec![field1, field2, field3, field4, field5],
287        )));
288        push(stack, variant)
289    }
290}
291
292/// Create a variant with 6 fields
293///
294/// Stack effect: ( field1 field2 field3 field4 field5 field6 Symbol -- Variant )
295///
296/// # Safety
297/// Stack must have 6 fields and Symbol tag on top
298#[unsafe(no_mangle)]
299pub unsafe extern "C" fn patch_seq_make_variant_6(stack: Stack) -> Stack {
300    use crate::value::VariantData;
301
302    unsafe {
303        let (stack, tag_val) = pop(stack);
304        let tag = match tag_val {
305            Value::Symbol(s) => s,
306            _ => panic!("make-variant-6: expected Symbol (tag), got {:?}", tag_val),
307        };
308
309        let (stack, field6) = pop(stack);
310        let (stack, field5) = pop(stack);
311        let (stack, field4) = pop(stack);
312        let (stack, field3) = pop(stack);
313        let (stack, field2) = pop(stack);
314        let (stack, field1) = pop(stack);
315        let variant = Value::Variant(Arc::new(VariantData::new(
316            tag,
317            vec![field1, field2, field3, field4, field5, field6],
318        )));
319        push(stack, variant)
320    }
321}
322
323/// Create a variant with 7 fields
324///
325/// Stack effect: ( field1 field2 field3 field4 field5 field6 field7 Symbol -- Variant )
326///
327/// # Safety
328/// Stack must have 7 fields and Symbol tag on top
329#[unsafe(no_mangle)]
330pub unsafe extern "C" fn patch_seq_make_variant_7(stack: Stack) -> Stack {
331    use crate::value::VariantData;
332
333    unsafe {
334        let (stack, tag_val) = pop(stack);
335        let tag = match tag_val {
336            Value::Symbol(s) => s,
337            _ => panic!("make-variant-7: expected Symbol (tag), got {:?}", tag_val),
338        };
339
340        let (stack, field7) = pop(stack);
341        let (stack, field6) = pop(stack);
342        let (stack, field5) = pop(stack);
343        let (stack, field4) = pop(stack);
344        let (stack, field3) = pop(stack);
345        let (stack, field2) = pop(stack);
346        let (stack, field1) = pop(stack);
347        let variant = Value::Variant(Arc::new(VariantData::new(
348            tag,
349            vec![field1, field2, field3, field4, field5, field6, field7],
350        )));
351        push(stack, variant)
352    }
353}
354
355/// Create a variant with 8 fields
356///
357/// Stack effect: ( field1 field2 field3 field4 field5 field6 field7 field8 Symbol -- Variant )
358///
359/// # Safety
360/// Stack must have 8 fields and Symbol tag on top
361#[unsafe(no_mangle)]
362pub unsafe extern "C" fn patch_seq_make_variant_8(stack: Stack) -> Stack {
363    use crate::value::VariantData;
364
365    unsafe {
366        let (stack, tag_val) = pop(stack);
367        let tag = match tag_val {
368            Value::Symbol(s) => s,
369            _ => panic!("make-variant-8: expected Symbol (tag), got {:?}", tag_val),
370        };
371
372        let (stack, field8) = pop(stack);
373        let (stack, field7) = pop(stack);
374        let (stack, field6) = pop(stack);
375        let (stack, field5) = pop(stack);
376        let (stack, field4) = pop(stack);
377        let (stack, field3) = pop(stack);
378        let (stack, field2) = pop(stack);
379        let (stack, field1) = pop(stack);
380        let variant = Value::Variant(Arc::new(VariantData::new(
381            tag,
382            vec![
383                field1, field2, field3, field4, field5, field6, field7, field8,
384            ],
385        )));
386        push(stack, variant)
387    }
388}
389
390/// Create a variant with 9 fields
391///
392/// Stack effect: ( field1 ... field9 Symbol -- Variant )
393///
394/// # Safety
395/// Stack must have 9 fields and Symbol tag on top
396#[unsafe(no_mangle)]
397pub unsafe extern "C" fn patch_seq_make_variant_9(stack: Stack) -> Stack {
398    use crate::value::VariantData;
399
400    unsafe {
401        let (stack, tag_val) = pop(stack);
402        let tag = match tag_val {
403            Value::Symbol(s) => s,
404            _ => panic!("make-variant-9: expected Symbol (tag), got {:?}", tag_val),
405        };
406
407        let (stack, field9) = pop(stack);
408        let (stack, field8) = pop(stack);
409        let (stack, field7) = pop(stack);
410        let (stack, field6) = pop(stack);
411        let (stack, field5) = pop(stack);
412        let (stack, field4) = pop(stack);
413        let (stack, field3) = pop(stack);
414        let (stack, field2) = pop(stack);
415        let (stack, field1) = pop(stack);
416        let variant = Value::Variant(Arc::new(VariantData::new(
417            tag,
418            vec![
419                field1, field2, field3, field4, field5, field6, field7, field8, field9,
420            ],
421        )));
422        push(stack, variant)
423    }
424}
425
426/// Create a variant with 10 fields
427///
428/// Stack effect: ( field1 ... field10 Symbol -- Variant )
429///
430/// # Safety
431/// Stack must have 10 fields and Symbol tag on top
432#[unsafe(no_mangle)]
433pub unsafe extern "C" fn patch_seq_make_variant_10(stack: Stack) -> Stack {
434    use crate::value::VariantData;
435
436    unsafe {
437        let (stack, tag_val) = pop(stack);
438        let tag = match tag_val {
439            Value::Symbol(s) => s,
440            _ => panic!("make-variant-10: expected Symbol (tag), got {:?}", tag_val),
441        };
442
443        let (stack, field10) = pop(stack);
444        let (stack, field9) = pop(stack);
445        let (stack, field8) = pop(stack);
446        let (stack, field7) = pop(stack);
447        let (stack, field6) = pop(stack);
448        let (stack, field5) = pop(stack);
449        let (stack, field4) = pop(stack);
450        let (stack, field3) = pop(stack);
451        let (stack, field2) = pop(stack);
452        let (stack, field1) = pop(stack);
453        let variant = Value::Variant(Arc::new(VariantData::new(
454            tag,
455            vec![
456                field1, field2, field3, field4, field5, field6, field7, field8, field9, field10,
457            ],
458        )));
459        push(stack, variant)
460    }
461}
462
463/// Create a variant with 11 fields
464///
465/// Stack effect: ( field1 ... field11 Symbol -- Variant )
466///
467/// # Safety
468/// Stack must have 11 fields and Symbol tag on top
469#[unsafe(no_mangle)]
470pub unsafe extern "C" fn patch_seq_make_variant_11(stack: Stack) -> Stack {
471    use crate::value::VariantData;
472
473    unsafe {
474        let (stack, tag_val) = pop(stack);
475        let tag = match tag_val {
476            Value::Symbol(s) => s,
477            _ => panic!("make-variant-11: expected Symbol (tag), got {:?}", tag_val),
478        };
479
480        let (stack, field11) = pop(stack);
481        let (stack, field10) = pop(stack);
482        let (stack, field9) = pop(stack);
483        let (stack, field8) = pop(stack);
484        let (stack, field7) = pop(stack);
485        let (stack, field6) = pop(stack);
486        let (stack, field5) = pop(stack);
487        let (stack, field4) = pop(stack);
488        let (stack, field3) = pop(stack);
489        let (stack, field2) = pop(stack);
490        let (stack, field1) = pop(stack);
491        let variant = Value::Variant(Arc::new(VariantData::new(
492            tag,
493            vec![
494                field1, field2, field3, field4, field5, field6, field7, field8, field9, field10,
495                field11,
496            ],
497        )));
498        push(stack, variant)
499    }
500}
501
502/// Create a variant with 12 fields
503///
504/// Stack effect: ( field1 ... field12 Symbol -- Variant )
505///
506/// # Safety
507/// Stack must have 12 fields and Symbol tag on top
508#[unsafe(no_mangle)]
509pub unsafe extern "C" fn patch_seq_make_variant_12(stack: Stack) -> Stack {
510    use crate::value::VariantData;
511
512    unsafe {
513        let (stack, tag_val) = pop(stack);
514        let tag = match tag_val {
515            Value::Symbol(s) => s,
516            _ => panic!("make-variant-12: expected Symbol (tag), got {:?}", tag_val),
517        };
518
519        let (stack, field12) = pop(stack);
520        let (stack, field11) = pop(stack);
521        let (stack, field10) = pop(stack);
522        let (stack, field9) = pop(stack);
523        let (stack, field8) = pop(stack);
524        let (stack, field7) = pop(stack);
525        let (stack, field6) = pop(stack);
526        let (stack, field5) = pop(stack);
527        let (stack, field4) = pop(stack);
528        let (stack, field3) = pop(stack);
529        let (stack, field2) = pop(stack);
530        let (stack, field1) = pop(stack);
531        let variant = Value::Variant(Arc::new(VariantData::new(
532            tag,
533            vec![
534                field1, field2, field3, field4, field5, field6, field7, field8, field9, field10,
535                field11, field12,
536            ],
537        )));
538        push(stack, variant)
539    }
540}
541
542// Re-exports for internal use
543pub use patch_seq_make_variant_0 as make_variant_0;
544pub use patch_seq_make_variant_1 as make_variant_1;
545pub use patch_seq_make_variant_2 as make_variant_2;
546pub use patch_seq_make_variant_3 as make_variant_3;
547pub use patch_seq_make_variant_4 as make_variant_4;
548pub use patch_seq_make_variant_5 as make_variant_5;
549pub use patch_seq_make_variant_6 as make_variant_6;
550pub use patch_seq_make_variant_7 as make_variant_7;
551pub use patch_seq_make_variant_8 as make_variant_8;
552pub use patch_seq_make_variant_9 as make_variant_9;
553pub use patch_seq_make_variant_10 as make_variant_10;
554pub use patch_seq_make_variant_11 as make_variant_11;
555pub use patch_seq_make_variant_12 as make_variant_12;
556
557/// Append a value to a variant, returning a new variant
558///
559/// Stack effect: ( Variant Value -- Variant' )
560///
561/// Creates a new variant with the same tag as the input, but with
562/// the new value appended to its fields. The original variant is
563/// not modified (functional/immutable style).
564///
565/// Example: For arrays, `[1, 2] 3 variant-append` produces `[1, 2, 3]`
566/// Example: For objects, `{} "key" variant-append "val" variant-append` builds `{"key": "val"}`
567///
568/// # Safety
569/// Stack must have a Variant and a Value on top
570#[unsafe(no_mangle)]
571pub unsafe extern "C" fn patch_seq_variant_append(stack: Stack) -> Stack {
572    use crate::value::VariantData;
573
574    unsafe {
575        // Fast path: peek at the variant at sp-2 without popping.
576        // SAFETY: variant.append requires two values on the stack (enforced by
577        // the type checker), so stack.sub(2) is valid.
578        if let Some(Value::Variant(variant_arc)) = peek_heap_mut_second(stack)
579            && let Some(data) = Arc::get_mut(variant_arc)
580        {
581            // Sole owner all the way down — mutate in place.
582            // Safety: `data` points into the Value at sp-2. `pop` only
583            // touches sp-1 (decrements sp, reads that slot), so sp-2's
584            // memory is not accessed or invalidated by the pop.
585            let (stack, value) = pop(stack);
586            data.fields.push(value);
587            return stack; // Variant is still at sp-1, mutated in place
588        }
589
590        // Slow path: pop both, clone if shared, push result
591        let (stack, value) = pop(stack);
592        let (stack, variant_val) = pop(stack);
593
594        match variant_val {
595            Value::Variant(mut arc) => {
596                if let Some(data) = Arc::get_mut(&mut arc) {
597                    data.fields.push(value);
598                    push(stack, Value::Variant(arc))
599                } else {
600                    let mut new_fields = Vec::with_capacity(arc.fields.len() + 1);
601                    new_fields.extend(arc.fields.iter().cloned());
602                    new_fields.push(value);
603                    let new_variant =
604                        Value::Variant(Arc::new(VariantData::new(arc.tag.clone(), new_fields)));
605                    push(stack, new_variant)
606                }
607            }
608            _ => panic!("variant-append: expected Variant, got {:?}", variant_val),
609        }
610    }
611}
612
613/// Get the last field from a variant
614///
615/// Stack effect: ( Variant -- Value )
616///
617/// Returns a clone of the last field. Panics if the variant has no fields.
618/// This is the "peek" operation for using variants as stacks.
619///
620/// # Safety
621/// Stack must have a Variant on top
622#[unsafe(no_mangle)]
623pub unsafe extern "C" fn patch_seq_variant_last(stack: Stack) -> Stack {
624    unsafe {
625        let (stack, variant_val) = pop(stack);
626
627        match variant_val {
628            Value::Variant(variant_data) => {
629                if variant_data.fields.is_empty() {
630                    panic!("variant-last: variant has no fields");
631                }
632
633                let last = variant_data.fields.last().unwrap().clone();
634                push(stack, last)
635            }
636            _ => panic!("variant-last: expected Variant, got {:?}", variant_val),
637        }
638    }
639}
640
641/// Get all but the last field from a variant
642///
643/// Stack effect: ( Variant -- Variant' )
644///
645/// Returns a new variant with the same tag but without the last field.
646/// Panics if the variant has no fields.
647/// This is the "pop" operation for using variants as stacks (without returning the popped value).
648///
649/// # Safety
650/// Stack must have a Variant on top
651#[unsafe(no_mangle)]
652pub unsafe extern "C" fn patch_seq_variant_init(stack: Stack) -> Stack {
653    use crate::value::VariantData;
654
655    unsafe {
656        let (stack, variant_val) = pop(stack);
657
658        match variant_val {
659            Value::Variant(variant_data) => {
660                if variant_data.fields.is_empty() {
661                    panic!("variant-init: variant has no fields");
662                }
663
664                // Create new fields without the last element
665                let new_fields: Vec<Value> =
666                    variant_data.fields[..variant_data.fields.len() - 1].to_vec();
667
668                let new_variant = Value::Variant(Arc::new(VariantData::new(
669                    variant_data.tag.clone(),
670                    new_fields,
671                )));
672
673                push(stack, new_variant)
674            }
675            _ => panic!("variant-init: expected Variant, got {:?}", variant_val),
676        }
677    }
678}
679
680/// Unpack a variant's fields onto the stack
681///
682/// Takes a field count as parameter and:
683/// - Pops the variant from the stack
684/// - Pushes each field (0..field_count) in order
685///
686/// Stack effect: ( Variant -- field0 field1 ... fieldN-1 )
687///
688/// Used by match expression codegen to extract variant fields.
689///
690/// # Safety
691/// Stack must have a Variant on top with at least `field_count` fields
692#[unsafe(no_mangle)]
693pub unsafe extern "C" fn patch_seq_unpack_variant(stack: Stack, field_count: i64) -> Stack {
694    unsafe {
695        let (mut stack, variant_val) = pop(stack);
696
697        match variant_val {
698            Value::Variant(variant_data) => {
699                let count = field_count as usize;
700                if count > variant_data.fields.len() {
701                    panic!(
702                        "unpack-variant: requested {} fields but variant only has {}",
703                        count,
704                        variant_data.fields.len()
705                    );
706                }
707
708                // Push each field in order (field0 first, then field1, etc.)
709                for i in 0..count {
710                    stack = push(stack, variant_data.fields[i].clone());
711                }
712
713                stack
714            }
715            _ => panic!("unpack-variant: expected Variant, got {:?}", variant_val),
716        }
717    }
718}
719
720// Public re-exports with short names for internal use
721pub use patch_seq_unpack_variant as unpack_variant;
722pub use patch_seq_variant_append as variant_append;
723pub use patch_seq_variant_field_at as variant_field_at;
724pub use patch_seq_variant_field_count as variant_field_count;
725pub use patch_seq_variant_init as variant_init;
726pub use patch_seq_variant_last as variant_last;
727pub use patch_seq_variant_tag as variant_tag;
728
729#[cfg(test)]
730mod tests {
731    use super::*;
732    use crate::seqstring::global_string;
733    use crate::value::VariantData;
734
735    #[test]
736    fn test_variant_field_count() {
737        unsafe {
738            // Create a variant with 3 fields
739            let variant = Value::Variant(Arc::new(VariantData::new(
740                global_string("TestTag".to_string()),
741                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
742            )));
743
744            let stack = crate::stack::alloc_test_stack();
745            let stack = push(stack, variant);
746            let stack = variant_field_count(stack);
747
748            let (_stack, result) = pop(stack);
749            assert_eq!(result, Value::Int(3));
750        }
751    }
752
753    #[test]
754    fn test_variant_tag() {
755        unsafe {
756            // Create a variant with tag "MyTag"
757            let variant = Value::Variant(Arc::new(VariantData::new(
758                global_string("MyTag".to_string()),
759                vec![Value::Int(10)],
760            )));
761
762            let stack = crate::stack::alloc_test_stack();
763            let stack = push(stack, variant);
764            let stack = variant_tag(stack);
765
766            let (_stack, result) = pop(stack);
767            assert_eq!(result, Value::Symbol(global_string("MyTag".to_string())));
768        }
769    }
770
771    #[test]
772    fn test_variant_field_at() {
773        unsafe {
774            let str1 = global_string("hello".to_string());
775            let str2 = global_string("world".to_string());
776
777            // Create a variant with mixed fields
778            let variant = Value::Variant(Arc::new(VariantData::new(
779                global_string("TestTag".to_string()),
780                vec![
781                    Value::String(str1.clone()),
782                    Value::Int(42),
783                    Value::String(str2.clone()),
784                ],
785            )));
786
787            // Test accessing field 0
788            let stack = crate::stack::alloc_test_stack();
789            let stack = push(stack, variant.clone());
790            let stack = push(stack, Value::Int(0));
791            let stack = variant_field_at(stack);
792
793            let (_stack, result) = pop(stack);
794            assert_eq!(result, Value::String(str1.clone()));
795
796            // Test accessing field 1
797            let stack = push(stack, variant.clone());
798            let stack = push(stack, Value::Int(1));
799            let stack = variant_field_at(stack);
800
801            let (_stack, result) = pop(stack);
802            assert_eq!(result, Value::Int(42));
803
804            // Test accessing field 2
805            let stack = push(stack, variant.clone());
806            let stack = push(stack, Value::Int(2));
807            let stack = variant_field_at(stack);
808
809            let (_stack, result) = pop(stack);
810            assert_eq!(result, Value::String(str2));
811        }
812    }
813
814    #[test]
815    fn test_variant_field_count_empty() {
816        unsafe {
817            // Create a variant with no fields
818            let variant = Value::Variant(Arc::new(VariantData::new(
819                global_string("Empty".to_string()),
820                vec![],
821            )));
822
823            let stack = crate::stack::alloc_test_stack();
824            let stack = push(stack, variant);
825            let stack = variant_field_count(stack);
826
827            let (_stack, result) = pop(stack);
828            assert_eq!(result, Value::Int(0));
829        }
830    }
831
832    #[test]
833    fn test_make_variant_with_fields() {
834        unsafe {
835            // Create: 10 20 30 :Tag make-variant-3
836            // Should produce variant with tag :Tag and fields [10, 20, 30]
837            let stack = crate::stack::alloc_test_stack();
838            let stack = push(stack, Value::Int(10)); // field 0
839            let stack = push(stack, Value::Int(20)); // field 1
840            let stack = push(stack, Value::Int(30)); // field 2
841            let stack = push(stack, Value::Symbol(global_string("Tag".to_string()))); // tag
842
843            let stack = make_variant_3(stack);
844
845            let (_stack, result) = pop(stack);
846
847            match result {
848                Value::Variant(v) => {
849                    assert_eq!(v.tag.as_str(), "Tag");
850                    assert_eq!(v.fields.len(), 3);
851                    assert_eq!(v.fields[0], Value::Int(10));
852                    assert_eq!(v.fields[1], Value::Int(20));
853                    assert_eq!(v.fields[2], Value::Int(30));
854                }
855                _ => panic!("Expected Variant"),
856            }
857        }
858    }
859
860    #[test]
861    fn test_make_variant_empty() {
862        unsafe {
863            // Create: :None make-variant-0
864            // Should produce variant with tag :None and no fields
865            let stack = crate::stack::alloc_test_stack();
866            let stack = push(stack, Value::Symbol(global_string("None".to_string()))); // tag
867
868            let stack = make_variant_0(stack);
869
870            let (_stack, result) = pop(stack);
871
872            match result {
873                Value::Variant(v) => {
874                    assert_eq!(v.tag.as_str(), "None");
875                    assert_eq!(v.fields.len(), 0);
876                }
877                _ => panic!("Expected Variant"),
878            }
879        }
880    }
881
882    #[test]
883    fn test_make_variant_with_mixed_types() {
884        unsafe {
885            let s = global_string("hello".to_string());
886
887            // Create variant with mixed field types using make-variant-3
888            let stack = crate::stack::alloc_test_stack();
889            let stack = push(stack, Value::Int(42));
890            let stack = push(stack, Value::String(s.clone()));
891            let stack = push(stack, Value::Float(3.5));
892            let stack = push(stack, Value::Symbol(global_string("Mixed".to_string()))); // tag
893
894            let stack = make_variant_3(stack);
895
896            let (_stack, result) = pop(stack);
897
898            match result {
899                Value::Variant(v) => {
900                    assert_eq!(v.tag.as_str(), "Mixed");
901                    assert_eq!(v.fields.len(), 3);
902                    assert_eq!(v.fields[0], Value::Int(42));
903                    assert_eq!(v.fields[1], Value::String(s));
904                    assert_eq!(v.fields[2], Value::Float(3.5));
905                }
906                _ => panic!("Expected Variant"),
907            }
908        }
909    }
910
911    #[test]
912    fn test_variant_append() {
913        unsafe {
914            // Create an empty variant (tag Array)
915            let stack = crate::stack::alloc_test_stack();
916            let stack = push(stack, Value::Symbol(global_string("Array".to_string()))); // tag
917            let stack = make_variant_0(stack);
918
919            // Append a value
920            let stack = push(stack, Value::Int(42));
921            let stack = variant_append(stack);
922
923            // Check result
924            let (_stack, result) = pop(stack);
925            match result {
926                Value::Variant(v) => {
927                    assert_eq!(v.tag.as_str(), "Array");
928                    assert_eq!(v.fields.len(), 1);
929                    assert_eq!(v.fields[0], Value::Int(42));
930                }
931                _ => panic!("Expected Variant"),
932            }
933        }
934    }
935
936    #[test]
937    fn test_variant_append_multiple() {
938        unsafe {
939            // Create an empty variant (tag Object)
940            let stack = crate::stack::alloc_test_stack();
941            let stack = push(stack, Value::Symbol(global_string("Object".to_string()))); // tag
942            let stack = make_variant_0(stack);
943
944            // Append key
945            let key = global_string("name".to_string());
946            let stack = push(stack, Value::String(key.clone()));
947            let stack = variant_append(stack);
948
949            // Append value
950            let val = global_string("John".to_string());
951            let stack = push(stack, Value::String(val.clone()));
952            let stack = variant_append(stack);
953
954            // Check result - should have 2 fields
955            let (_stack, result) = pop(stack);
956            match result {
957                Value::Variant(v) => {
958                    assert_eq!(v.tag.as_str(), "Object");
959                    assert_eq!(v.fields.len(), 2);
960                    assert_eq!(v.fields[0], Value::String(key));
961                    assert_eq!(v.fields[1], Value::String(val));
962                }
963                _ => panic!("Expected Variant"),
964            }
965        }
966    }
967
968    #[test]
969    fn test_variant_last() {
970        unsafe {
971            // Create a variant with 3 fields
972            let variant = Value::Variant(Arc::new(VariantData::new(
973                global_string("List".to_string()),
974                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
975            )));
976
977            let stack = crate::stack::alloc_test_stack();
978            let stack = push(stack, variant);
979            let stack = variant_last(stack);
980
981            let (_stack, result) = pop(stack);
982            assert_eq!(result, Value::Int(30));
983        }
984    }
985
986    #[test]
987    fn test_variant_init() {
988        unsafe {
989            // Create a variant with 3 fields
990            let variant = Value::Variant(Arc::new(VariantData::new(
991                global_string("Custom".to_string()),
992                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
993            )));
994
995            let stack = crate::stack::alloc_test_stack();
996            let stack = push(stack, variant);
997            let stack = variant_init(stack);
998
999            let (_stack, result) = pop(stack);
1000            match result {
1001                Value::Variant(v) => {
1002                    assert_eq!(v.tag.as_str(), "Custom"); // tag preserved
1003                    assert_eq!(v.fields.len(), 2);
1004                    assert_eq!(v.fields[0], Value::Int(10));
1005                    assert_eq!(v.fields[1], Value::Int(20));
1006                }
1007                _ => panic!("Expected Variant"),
1008            }
1009        }
1010    }
1011
1012    #[test]
1013    fn test_variant_stack_operations() {
1014        // Test using variant as a stack: append, append, last, init, last
1015        unsafe {
1016            // Create empty "stack" variant (tag Stack)
1017            let stack = crate::stack::alloc_test_stack();
1018            let stack = push(stack, Value::Symbol(global_string("Stack".to_string()))); // tag
1019            let stack = make_variant_0(stack);
1020
1021            // Append 10
1022            let stack = push(stack, Value::Int(10));
1023            let stack = variant_append(stack);
1024
1025            // Append 20
1026            let stack = push(stack, Value::Int(20));
1027            let stack = variant_append(stack);
1028
1029            // Now have variant with [10, 20] on stack
1030            // Dup and get last (should be 20)
1031            let (stack, variant) = pop(stack);
1032            let stack = push(stack, variant.clone());
1033            let stack = push(stack, variant);
1034            let stack = variant_last(stack);
1035            let (stack, top) = pop(stack);
1036            assert_eq!(top, Value::Int(20));
1037
1038            // Now use init to remove last element
1039            let stack = variant_init(stack);
1040
1041            // Dup and get last (should now be 10)
1042            let (stack, variant) = pop(stack);
1043            let stack = push(stack, variant.clone());
1044            let stack = push(stack, variant);
1045            let stack = variant_last(stack);
1046            let (stack, top) = pop(stack);
1047            assert_eq!(top, Value::Int(10));
1048
1049            // Verify remaining variant has 1 field
1050            let (_stack, result) = pop(stack);
1051            match result {
1052                Value::Variant(v) => {
1053                    assert_eq!(v.fields.len(), 1);
1054                    assert_eq!(v.fields[0], Value::Int(10));
1055                }
1056                _ => panic!("Expected Variant"),
1057            }
1058        }
1059    }
1060
1061    #[test]
1062    fn test_variant_clone_is_o1() {
1063        // Regression test: Ensure deeply nested variants clone in O(1) time
1064        // This would have been O(2^n) with Box before the Arc change
1065        let mut variant = Value::Variant(Arc::new(VariantData::new(
1066            global_string("Level0".to_string()),
1067            vec![],
1068        )));
1069
1070        // Build a deeply nested structure (100 levels)
1071        for i in 0..100 {
1072            variant = Value::Variant(Arc::new(VariantData::new(
1073                global_string(format!("Level{}", i)),
1074                vec![variant.clone()],
1075            )));
1076        }
1077
1078        // Clone should be O(1) - just incrementing Arc refcount
1079        let start = std::time::Instant::now();
1080        for _ in 0..1000 {
1081            let _copy = variant.clone();
1082        }
1083        let elapsed = start.elapsed();
1084
1085        // 1000 clones of a 100-deep structure should take < 1ms with Arc
1086        // With Box it would take seconds or longer
1087        assert!(
1088            elapsed.as_millis() < 10,
1089            "Clone took {:?} - should be O(1) with Arc",
1090            elapsed
1091        );
1092    }
1093
1094    #[test]
1095    fn test_variant_arc_sharing() {
1096        // Test that Arc properly shares data (refcount increases, not deep copy)
1097        let inner = Value::Variant(Arc::new(VariantData::new(
1098            global_string("Inner".to_string()),
1099            vec![Value::Int(42)],
1100        )));
1101        let outer = Value::Variant(Arc::new(VariantData::new(
1102            global_string("Outer".to_string()),
1103            vec![inner.clone()],
1104        )));
1105
1106        // Clone should share the same Arc, not deep copy
1107        let outer_clone = outer.clone();
1108
1109        // Both should have the same inner value
1110        match (&outer, &outer_clone) {
1111            (Value::Variant(a), Value::Variant(b)) => {
1112                // Arc::ptr_eq would be ideal but fields are Box<[Value]>
1113                // Instead verify the values are equal (they share the same data)
1114                assert_eq!(a.tag, b.tag);
1115                assert_eq!(a.fields.len(), b.fields.len());
1116            }
1117            _ => panic!("Expected Variants"),
1118        }
1119    }
1120
1121    #[test]
1122    fn test_variant_thread_safe_sharing() {
1123        // Test that variants can be safely shared between threads
1124        // This validates the Send + Sync implementation
1125        use std::sync::Arc as StdArc;
1126        use std::thread;
1127
1128        let variant = Value::Variant(Arc::new(VariantData::new(
1129            global_string("ThreadSafe".to_string()),
1130            vec![Value::Int(1), Value::Int(2), Value::Int(3)],
1131        )));
1132
1133        // Wrap in Arc for thread sharing
1134        let shared = StdArc::new(variant);
1135
1136        let handles: Vec<_> = (0..4)
1137            .map(|_| {
1138                let v = StdArc::clone(&shared);
1139                thread::spawn(move || {
1140                    // Access the variant from another thread
1141                    match &*v {
1142                        Value::Variant(data) => {
1143                            assert_eq!(data.tag.as_str(), "ThreadSafe");
1144                            assert_eq!(data.fields.len(), 3);
1145                        }
1146                        _ => panic!("Expected Variant"),
1147                    }
1148                })
1149            })
1150            .collect();
1151
1152        for h in handles {
1153            h.join().expect("Thread panicked");
1154        }
1155    }
1156}