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, pop, push};
7use crate::value::Value;
8
9/// Get the number of fields in a variant
10///
11/// Stack effect: ( Variant -- Int )
12///
13/// # Safety
14/// Stack must have a Variant on top
15#[unsafe(no_mangle)]
16pub unsafe extern "C" fn patch_seq_variant_field_count(stack: Stack) -> Stack {
17    unsafe {
18        let (stack, value) = pop(stack);
19
20        match value {
21            Value::Variant(variant_data) => {
22                let count = variant_data.fields.len() as i64;
23                push(stack, Value::Int(count))
24            }
25            _ => panic!("variant-field-count: expected Variant, got {:?}", value),
26        }
27    }
28}
29
30/// Get the tag of a variant
31///
32/// Stack effect: ( Variant -- Int )
33///
34/// # Safety
35/// Stack must have a Variant on top
36#[unsafe(no_mangle)]
37pub unsafe extern "C" fn patch_seq_variant_tag(stack: Stack) -> Stack {
38    unsafe {
39        let (stack, value) = pop(stack);
40
41        match value {
42            Value::Variant(variant_data) => {
43                let tag = variant_data.tag as i64;
44                push(stack, Value::Int(tag))
45            }
46            _ => panic!("variant-tag: expected Variant, got {:?}", value),
47        }
48    }
49}
50
51/// Get a field from a variant at the given index
52///
53/// Stack effect: ( Variant Int -- Value )
54///
55/// Returns a clone of the field value at the specified index.
56/// Panics if index is out of bounds.
57///
58/// # Safety
59/// Stack must have a Variant and Int on top
60#[unsafe(no_mangle)]
61pub unsafe extern "C" fn patch_seq_variant_field_at(stack: Stack) -> Stack {
62    unsafe {
63        let (stack, index_val) = pop(stack);
64        let index = match index_val {
65            Value::Int(i) => i,
66            _ => panic!(
67                "variant-field-at: expected Int (index), got {:?}",
68                index_val
69            ),
70        };
71
72        if index < 0 {
73            panic!("variant-field-at: index cannot be negative: {}", index);
74        }
75
76        let (stack, variant_val) = pop(stack);
77
78        match variant_val {
79            Value::Variant(variant_data) => {
80                let idx = index as usize;
81                if idx >= variant_data.fields.len() {
82                    panic!(
83                        "variant-field-at: index {} out of bounds (variant has {} fields)",
84                        index,
85                        variant_data.fields.len()
86                    );
87                }
88
89                // Clone the field value and push it
90                let field = variant_data.fields[idx].clone();
91                push(stack, field)
92            }
93            _ => panic!("variant-field-at: expected Variant, got {:?}", variant_val),
94        }
95    }
96}
97
98/// Create a variant with the given tag and fields
99///
100/// Stack effect: ( field1 ... fieldN count tag -- Variant )
101///
102/// Pops `count` values from the stack as fields (in reverse order),
103/// then creates a Variant with the given tag.
104///
105/// Example: `10 20 30 3 42 make-variant` creates a variant with
106/// tag 42 and fields [10, 20, 30].
107///
108/// # Safety
109/// Stack must have tag, count, and count values on top
110#[unsafe(no_mangle)]
111pub unsafe extern "C" fn patch_seq_make_variant(stack: Stack) -> Stack {
112    use crate::value::VariantData;
113
114    unsafe {
115        // Pop tag
116        let (stack, tag_val) = pop(stack);
117        let tag = match tag_val {
118            Value::Int(t) => {
119                if t < 0 {
120                    panic!("make-variant: tag cannot be negative: {}", t);
121                }
122                t as u32
123            }
124            _ => panic!("make-variant: expected Int (tag), got {:?}", tag_val),
125        };
126
127        // Pop count
128        let (stack, count_val) = pop(stack);
129        let count = match count_val {
130            Value::Int(c) => {
131                if c < 0 {
132                    panic!("make-variant: count cannot be negative: {}", c);
133                }
134                c as usize
135            }
136            _ => panic!("make-variant: expected Int (count), got {:?}", count_val),
137        };
138
139        // Pop count values (they come off in reverse order)
140        let mut fields = Vec::with_capacity(count);
141        let mut current_stack = stack;
142
143        for i in 0..count {
144            if current_stack.is_null() {
145                panic!(
146                    "make-variant: stack underflow, expected {} fields but only got {}",
147                    count, i
148                );
149            }
150            let (new_stack, value) = pop(current_stack);
151            fields.push(value);
152            current_stack = new_stack;
153        }
154
155        // Reverse to get original order (first pushed = first field)
156        fields.reverse();
157
158        // Create and push the variant
159        let variant = Value::Variant(Box::new(VariantData::new(tag, fields)));
160        push(current_stack, variant)
161    }
162}
163
164// ============================================================================
165// Type-safe variant constructors with fixed arity
166// ============================================================================
167
168/// Create a variant with 0 fields (just a tag)
169///
170/// Stack effect: ( tag -- Variant )
171///
172/// # Safety
173/// Stack must have at least one Int (the tag) on top
174#[unsafe(no_mangle)]
175pub unsafe extern "C" fn patch_seq_make_variant_0(stack: Stack) -> Stack {
176    use crate::value::VariantData;
177
178    unsafe {
179        let (stack, tag_val) = pop(stack);
180        let tag = match tag_val {
181            Value::Int(t) => {
182                if t < 0 {
183                    panic!("make-variant-0: tag cannot be negative: {}", t);
184                }
185                t as u32
186            }
187            _ => panic!("make-variant-0: expected Int (tag), got {:?}", tag_val),
188        };
189
190        let variant = Value::Variant(Box::new(VariantData::new(tag, vec![])));
191        push(stack, variant)
192    }
193}
194
195/// Create a variant with 1 field
196///
197/// Stack effect: ( field1 tag -- Variant )
198///
199/// # Safety
200/// Stack must have field1 and tag on top
201#[unsafe(no_mangle)]
202pub unsafe extern "C" fn patch_seq_make_variant_1(stack: Stack) -> Stack {
203    use crate::value::VariantData;
204
205    unsafe {
206        let (stack, tag_val) = pop(stack);
207        let tag = match tag_val {
208            Value::Int(t) => {
209                if t < 0 {
210                    panic!("make-variant-1: tag cannot be negative: {}", t);
211                }
212                t as u32
213            }
214            _ => panic!("make-variant-1: expected Int (tag), got {:?}", tag_val),
215        };
216
217        let (stack, field1) = pop(stack);
218        let variant = Value::Variant(Box::new(VariantData::new(tag, vec![field1])));
219        push(stack, variant)
220    }
221}
222
223/// Create a variant with 2 fields
224///
225/// Stack effect: ( field1 field2 tag -- Variant )
226///
227/// # Safety
228/// Stack must have field1, field2, and tag on top
229#[unsafe(no_mangle)]
230pub unsafe extern "C" fn patch_seq_make_variant_2(stack: Stack) -> Stack {
231    use crate::value::VariantData;
232
233    unsafe {
234        let (stack, tag_val) = pop(stack);
235        let tag = match tag_val {
236            Value::Int(t) => {
237                if t < 0 {
238                    panic!("make-variant-2: tag cannot be negative: {}", t);
239                }
240                t as u32
241            }
242            _ => panic!("make-variant-2: expected Int (tag), got {:?}", tag_val),
243        };
244
245        let (stack, field2) = pop(stack);
246        let (stack, field1) = pop(stack);
247        let variant = Value::Variant(Box::new(VariantData::new(tag, vec![field1, field2])));
248        push(stack, variant)
249    }
250}
251
252/// Create a variant with 3 fields
253///
254/// Stack effect: ( field1 field2 field3 tag -- Variant )
255///
256/// # Safety
257/// Stack must have field1, field2, field3, and tag on top
258#[unsafe(no_mangle)]
259pub unsafe extern "C" fn patch_seq_make_variant_3(stack: Stack) -> Stack {
260    use crate::value::VariantData;
261
262    unsafe {
263        let (stack, tag_val) = pop(stack);
264        let tag = match tag_val {
265            Value::Int(t) => {
266                if t < 0 {
267                    panic!("make-variant-3: tag cannot be negative: {}", t);
268                }
269                t as u32
270            }
271            _ => panic!("make-variant-3: expected Int (tag), got {:?}", tag_val),
272        };
273
274        let (stack, field3) = pop(stack);
275        let (stack, field2) = pop(stack);
276        let (stack, field1) = pop(stack);
277        let variant = Value::Variant(Box::new(VariantData::new(
278            tag,
279            vec![field1, field2, field3],
280        )));
281        push(stack, variant)
282    }
283}
284
285/// Create a variant with 4 fields
286///
287/// Stack effect: ( field1 field2 field3 field4 tag -- Variant )
288///
289/// # Safety
290/// Stack must have field1, field2, field3, field4, and tag on top
291#[unsafe(no_mangle)]
292pub unsafe extern "C" fn patch_seq_make_variant_4(stack: Stack) -> Stack {
293    use crate::value::VariantData;
294
295    unsafe {
296        let (stack, tag_val) = pop(stack);
297        let tag = match tag_val {
298            Value::Int(t) => {
299                if t < 0 {
300                    panic!("make-variant-4: tag cannot be negative: {}", t);
301                }
302                t as u32
303            }
304            _ => panic!("make-variant-4: expected Int (tag), got {:?}", tag_val),
305        };
306
307        let (stack, field4) = pop(stack);
308        let (stack, field3) = pop(stack);
309        let (stack, field2) = pop(stack);
310        let (stack, field1) = pop(stack);
311        let variant = Value::Variant(Box::new(VariantData::new(
312            tag,
313            vec![field1, field2, field3, field4],
314        )));
315        push(stack, variant)
316    }
317}
318
319// Re-exports for internal use
320pub use patch_seq_make_variant_0 as make_variant_0;
321pub use patch_seq_make_variant_1 as make_variant_1;
322pub use patch_seq_make_variant_2 as make_variant_2;
323pub use patch_seq_make_variant_3 as make_variant_3;
324pub use patch_seq_make_variant_4 as make_variant_4;
325
326/// Append a value to a variant, returning a new variant
327///
328/// Stack effect: ( Variant Value -- Variant' )
329///
330/// Creates a new variant with the same tag as the input, but with
331/// the new value appended to its fields. The original variant is
332/// not modified (functional/immutable style).
333///
334/// Example: For arrays, `[1, 2] 3 variant-append` produces `[1, 2, 3]`
335/// Example: For objects, `{} "key" variant-append "val" variant-append` builds `{"key": "val"}`
336///
337/// # Safety
338/// Stack must have a Variant and a Value on top
339#[unsafe(no_mangle)]
340pub unsafe extern "C" fn patch_seq_variant_append(stack: Stack) -> Stack {
341    use crate::value::VariantData;
342
343    unsafe {
344        // Pop the value to append
345        let (stack, value) = pop(stack);
346
347        // Pop the variant
348        let (stack, variant_val) = pop(stack);
349
350        match variant_val {
351            Value::Variant(variant_data) => {
352                // Create new fields vector with the appended value
353                let mut new_fields = variant_data.fields.to_vec();
354                new_fields.push(value);
355
356                // Create new variant with same tag
357                let new_variant =
358                    Value::Variant(Box::new(VariantData::new(variant_data.tag, new_fields)));
359
360                push(stack, new_variant)
361            }
362            _ => panic!("variant-append: expected Variant, got {:?}", variant_val),
363        }
364    }
365}
366
367/// Get the last field from a variant
368///
369/// Stack effect: ( Variant -- Value )
370///
371/// Returns a clone of the last field. Panics if the variant has no fields.
372/// This is the "peek" operation for using variants as stacks.
373///
374/// # Safety
375/// Stack must have a Variant on top
376#[unsafe(no_mangle)]
377pub unsafe extern "C" fn patch_seq_variant_last(stack: Stack) -> Stack {
378    unsafe {
379        let (stack, variant_val) = pop(stack);
380
381        match variant_val {
382            Value::Variant(variant_data) => {
383                if variant_data.fields.is_empty() {
384                    panic!("variant-last: variant has no fields");
385                }
386
387                let last = variant_data.fields.last().unwrap().clone();
388                push(stack, last)
389            }
390            _ => panic!("variant-last: expected Variant, got {:?}", variant_val),
391        }
392    }
393}
394
395/// Get all but the last field from a variant
396///
397/// Stack effect: ( Variant -- Variant' )
398///
399/// Returns a new variant with the same tag but without the last field.
400/// Panics if the variant has no fields.
401/// This is the "pop" operation for using variants as stacks (without returning the popped value).
402///
403/// # Safety
404/// Stack must have a Variant on top
405#[unsafe(no_mangle)]
406pub unsafe extern "C" fn patch_seq_variant_init(stack: Stack) -> Stack {
407    use crate::value::VariantData;
408
409    unsafe {
410        let (stack, variant_val) = pop(stack);
411
412        match variant_val {
413            Value::Variant(variant_data) => {
414                if variant_data.fields.is_empty() {
415                    panic!("variant-init: variant has no fields");
416                }
417
418                // Create new fields without the last element
419                let new_fields: Vec<Value> =
420                    variant_data.fields[..variant_data.fields.len() - 1].to_vec();
421
422                let new_variant =
423                    Value::Variant(Box::new(VariantData::new(variant_data.tag, new_fields)));
424
425                push(stack, new_variant)
426            }
427            _ => panic!("variant-init: expected Variant, got {:?}", variant_val),
428        }
429    }
430}
431
432// Public re-exports with short names for internal use
433pub use patch_seq_make_variant as make_variant;
434pub use patch_seq_variant_append as variant_append;
435pub use patch_seq_variant_field_at as variant_field_at;
436pub use patch_seq_variant_field_count as variant_field_count;
437pub use patch_seq_variant_init as variant_init;
438pub use patch_seq_variant_last as variant_last;
439pub use patch_seq_variant_tag as variant_tag;
440
441#[cfg(test)]
442mod tests {
443    use super::*;
444    use crate::seqstring::global_string;
445    use crate::value::VariantData;
446
447    #[test]
448    fn test_variant_field_count() {
449        unsafe {
450            // Create a variant with 3 fields
451            let variant = Value::Variant(Box::new(VariantData::new(
452                0,
453                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
454            )));
455
456            let stack = std::ptr::null_mut();
457            let stack = push(stack, variant);
458            let stack = variant_field_count(stack);
459
460            let (stack, result) = pop(stack);
461            assert_eq!(result, Value::Int(3));
462            assert!(stack.is_null());
463        }
464    }
465
466    #[test]
467    fn test_variant_tag() {
468        unsafe {
469            // Create a variant with tag 42
470            let variant = Value::Variant(Box::new(VariantData::new(42, vec![Value::Int(10)])));
471
472            let stack = std::ptr::null_mut();
473            let stack = push(stack, variant);
474            let stack = variant_tag(stack);
475
476            let (stack, result) = pop(stack);
477            assert_eq!(result, Value::Int(42));
478            assert!(stack.is_null());
479        }
480    }
481
482    #[test]
483    fn test_variant_field_at() {
484        unsafe {
485            let str1 = global_string("hello".to_string());
486            let str2 = global_string("world".to_string());
487
488            // Create a variant with mixed fields
489            let variant = Value::Variant(Box::new(VariantData::new(
490                0,
491                vec![
492                    Value::String(str1.clone()),
493                    Value::Int(42),
494                    Value::String(str2.clone()),
495                ],
496            )));
497
498            // Test accessing field 0
499            let stack = std::ptr::null_mut();
500            let stack = push(stack, variant.clone());
501            let stack = push(stack, Value::Int(0));
502            let stack = variant_field_at(stack);
503
504            let (stack, result) = pop(stack);
505            assert_eq!(result, Value::String(str1.clone()));
506            assert!(stack.is_null());
507
508            // Test accessing field 1
509            let stack = push(stack, variant.clone());
510            let stack = push(stack, Value::Int(1));
511            let stack = variant_field_at(stack);
512
513            let (stack, result) = pop(stack);
514            assert_eq!(result, Value::Int(42));
515            assert!(stack.is_null());
516
517            // Test accessing field 2
518            let stack = push(stack, variant.clone());
519            let stack = push(stack, Value::Int(2));
520            let stack = variant_field_at(stack);
521
522            let (stack, result) = pop(stack);
523            assert_eq!(result, Value::String(str2));
524            assert!(stack.is_null());
525        }
526    }
527
528    #[test]
529    fn test_variant_field_count_empty() {
530        unsafe {
531            // Create a variant with no fields
532            let variant = Value::Variant(Box::new(VariantData::new(0, vec![])));
533
534            let stack = std::ptr::null_mut();
535            let stack = push(stack, variant);
536            let stack = variant_field_count(stack);
537
538            let (stack, result) = pop(stack);
539            assert_eq!(result, Value::Int(0));
540            assert!(stack.is_null());
541        }
542    }
543
544    #[test]
545    fn test_make_variant_with_fields() {
546        unsafe {
547            // Create: 10 20 30 3 42 make-variant
548            // Should produce variant with tag 42 and fields [10, 20, 30]
549            let stack = std::ptr::null_mut();
550            let stack = push(stack, Value::Int(10)); // field 0
551            let stack = push(stack, Value::Int(20)); // field 1
552            let stack = push(stack, Value::Int(30)); // field 2
553            let stack = push(stack, Value::Int(3)); // count
554            let stack = push(stack, Value::Int(42)); // tag
555
556            let stack = make_variant(stack);
557
558            let (stack, result) = pop(stack);
559
560            match result {
561                Value::Variant(v) => {
562                    assert_eq!(v.tag, 42);
563                    assert_eq!(v.fields.len(), 3);
564                    assert_eq!(v.fields[0], Value::Int(10));
565                    assert_eq!(v.fields[1], Value::Int(20));
566                    assert_eq!(v.fields[2], Value::Int(30));
567                }
568                _ => panic!("Expected Variant"),
569            }
570            assert!(stack.is_null());
571        }
572    }
573
574    #[test]
575    fn test_make_variant_empty() {
576        unsafe {
577            // Create: 0 0 make-variant
578            // Should produce variant with tag 0 and no fields
579            let stack = std::ptr::null_mut();
580            let stack = push(stack, Value::Int(0)); // count
581            let stack = push(stack, Value::Int(0)); // tag
582
583            let stack = make_variant(stack);
584
585            let (stack, result) = pop(stack);
586
587            match result {
588                Value::Variant(v) => {
589                    assert_eq!(v.tag, 0);
590                    assert_eq!(v.fields.len(), 0);
591                }
592                _ => panic!("Expected Variant"),
593            }
594            assert!(stack.is_null());
595        }
596    }
597
598    #[test]
599    fn test_make_variant_with_mixed_types() {
600        unsafe {
601            let s = global_string("hello".to_string());
602
603            // Create variant with mixed field types
604            let stack = std::ptr::null_mut();
605            let stack = push(stack, Value::Int(42));
606            let stack = push(stack, Value::String(s.clone()));
607            let stack = push(stack, Value::Float(3.5));
608            let stack = push(stack, Value::Int(3)); // count
609            let stack = push(stack, Value::Int(1)); // tag
610
611            let stack = make_variant(stack);
612
613            let (stack, result) = pop(stack);
614
615            match result {
616                Value::Variant(v) => {
617                    assert_eq!(v.tag, 1);
618                    assert_eq!(v.fields.len(), 3);
619                    assert_eq!(v.fields[0], Value::Int(42));
620                    assert_eq!(v.fields[1], Value::String(s));
621                    assert_eq!(v.fields[2], Value::Float(3.5));
622                }
623                _ => panic!("Expected Variant"),
624            }
625            assert!(stack.is_null());
626        }
627    }
628
629    #[test]
630    fn test_variant_append() {
631        unsafe {
632            // Create an empty variant (tag 4 for array)
633            let stack = std::ptr::null_mut();
634            let stack = push(stack, Value::Int(0)); // count
635            let stack = push(stack, Value::Int(4)); // tag (array)
636            let stack = make_variant(stack);
637
638            // Append a value
639            let stack = push(stack, Value::Int(42));
640            let stack = variant_append(stack);
641
642            // Check result
643            let (stack, result) = pop(stack);
644            match result {
645                Value::Variant(v) => {
646                    assert_eq!(v.tag, 4);
647                    assert_eq!(v.fields.len(), 1);
648                    assert_eq!(v.fields[0], Value::Int(42));
649                }
650                _ => panic!("Expected Variant"),
651            }
652            assert!(stack.is_null());
653        }
654    }
655
656    #[test]
657    fn test_variant_append_multiple() {
658        unsafe {
659            // Create an empty variant (tag 5 for object)
660            let stack = std::ptr::null_mut();
661            let stack = push(stack, Value::Int(0)); // count
662            let stack = push(stack, Value::Int(5)); // tag (object)
663            let stack = make_variant(stack);
664
665            // Append key
666            let key = global_string("name".to_string());
667            let stack = push(stack, Value::String(key.clone()));
668            let stack = variant_append(stack);
669
670            // Append value
671            let val = global_string("John".to_string());
672            let stack = push(stack, Value::String(val.clone()));
673            let stack = variant_append(stack);
674
675            // Check result - should have 2 fields
676            let (stack, result) = pop(stack);
677            match result {
678                Value::Variant(v) => {
679                    assert_eq!(v.tag, 5);
680                    assert_eq!(v.fields.len(), 2);
681                    assert_eq!(v.fields[0], Value::String(key));
682                    assert_eq!(v.fields[1], Value::String(val));
683                }
684                _ => panic!("Expected Variant"),
685            }
686            assert!(stack.is_null());
687        }
688    }
689
690    #[test]
691    fn test_variant_last() {
692        unsafe {
693            // Create a variant with 3 fields
694            let variant = Value::Variant(Box::new(VariantData::new(
695                0,
696                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
697            )));
698
699            let stack = std::ptr::null_mut();
700            let stack = push(stack, variant);
701            let stack = variant_last(stack);
702
703            let (stack, result) = pop(stack);
704            assert_eq!(result, Value::Int(30));
705            assert!(stack.is_null());
706        }
707    }
708
709    #[test]
710    fn test_variant_init() {
711        unsafe {
712            // Create a variant with 3 fields
713            let variant = Value::Variant(Box::new(VariantData::new(
714                42,
715                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
716            )));
717
718            let stack = std::ptr::null_mut();
719            let stack = push(stack, variant);
720            let stack = variant_init(stack);
721
722            let (stack, result) = pop(stack);
723            match result {
724                Value::Variant(v) => {
725                    assert_eq!(v.tag, 42); // tag preserved
726                    assert_eq!(v.fields.len(), 2);
727                    assert_eq!(v.fields[0], Value::Int(10));
728                    assert_eq!(v.fields[1], Value::Int(20));
729                }
730                _ => panic!("Expected Variant"),
731            }
732            assert!(stack.is_null());
733        }
734    }
735
736    #[test]
737    fn test_variant_stack_operations() {
738        // Test using variant as a stack: append, append, last, init, last
739        unsafe {
740            // Create empty "stack" variant (tag 99)
741            let stack = std::ptr::null_mut();
742            let stack = push(stack, Value::Int(0)); // count
743            let stack = push(stack, Value::Int(99)); // tag
744            let stack = make_variant(stack);
745
746            // Append 10
747            let stack = push(stack, Value::Int(10));
748            let stack = variant_append(stack);
749
750            // Append 20
751            let stack = push(stack, Value::Int(20));
752            let stack = variant_append(stack);
753
754            // Now have variant with [10, 20] on stack
755            // Dup and get last (should be 20)
756            let (stack, variant) = pop(stack);
757            let stack = push(stack, variant.clone());
758            let stack = push(stack, variant);
759            let stack = variant_last(stack);
760            let (stack, top) = pop(stack);
761            assert_eq!(top, Value::Int(20));
762
763            // Now use init to remove last element
764            let stack = variant_init(stack);
765
766            // Dup and get last (should now be 10)
767            let (stack, variant) = pop(stack);
768            let stack = push(stack, variant.clone());
769            let stack = push(stack, variant);
770            let stack = variant_last(stack);
771            let (stack, top) = pop(stack);
772            assert_eq!(top, Value::Int(10));
773
774            // Verify remaining variant has 1 field
775            let (stack, result) = pop(stack);
776            match result {
777                Value::Variant(v) => {
778                    assert_eq!(v.fields.len(), 1);
779                    assert_eq!(v.fields[0], Value::Int(10));
780                }
781                _ => panic!("Expected Variant"),
782            }
783            assert!(stack.is_null());
784        }
785    }
786}