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/// Append a value to a variant, returning a new variant
165///
166/// Stack effect: ( Variant Value -- Variant' )
167///
168/// Creates a new variant with the same tag as the input, but with
169/// the new value appended to its fields. The original variant is
170/// not modified (functional/immutable style).
171///
172/// Example: For arrays, `[1, 2] 3 variant-append` produces `[1, 2, 3]`
173/// Example: For objects, `{} "key" variant-append "val" variant-append` builds `{"key": "val"}`
174///
175/// # Safety
176/// Stack must have a Variant and a Value on top
177#[unsafe(no_mangle)]
178pub unsafe extern "C" fn patch_seq_variant_append(stack: Stack) -> Stack {
179    use crate::value::VariantData;
180
181    unsafe {
182        // Pop the value to append
183        let (stack, value) = pop(stack);
184
185        // Pop the variant
186        let (stack, variant_val) = pop(stack);
187
188        match variant_val {
189            Value::Variant(variant_data) => {
190                // Create new fields vector with the appended value
191                let mut new_fields = variant_data.fields.to_vec();
192                new_fields.push(value);
193
194                // Create new variant with same tag
195                let new_variant =
196                    Value::Variant(Box::new(VariantData::new(variant_data.tag, new_fields)));
197
198                push(stack, new_variant)
199            }
200            _ => panic!("variant-append: expected Variant, got {:?}", variant_val),
201        }
202    }
203}
204
205/// Get the last field from a variant
206///
207/// Stack effect: ( Variant -- Value )
208///
209/// Returns a clone of the last field. Panics if the variant has no fields.
210/// This is the "peek" operation for using variants as stacks.
211///
212/// # Safety
213/// Stack must have a Variant on top
214#[unsafe(no_mangle)]
215pub unsafe extern "C" fn patch_seq_variant_last(stack: Stack) -> Stack {
216    unsafe {
217        let (stack, variant_val) = pop(stack);
218
219        match variant_val {
220            Value::Variant(variant_data) => {
221                if variant_data.fields.is_empty() {
222                    panic!("variant-last: variant has no fields");
223                }
224
225                let last = variant_data.fields.last().unwrap().clone();
226                push(stack, last)
227            }
228            _ => panic!("variant-last: expected Variant, got {:?}", variant_val),
229        }
230    }
231}
232
233/// Get all but the last field from a variant
234///
235/// Stack effect: ( Variant -- Variant' )
236///
237/// Returns a new variant with the same tag but without the last field.
238/// Panics if the variant has no fields.
239/// This is the "pop" operation for using variants as stacks (without returning the popped value).
240///
241/// # Safety
242/// Stack must have a Variant on top
243#[unsafe(no_mangle)]
244pub unsafe extern "C" fn patch_seq_variant_init(stack: Stack) -> Stack {
245    use crate::value::VariantData;
246
247    unsafe {
248        let (stack, variant_val) = pop(stack);
249
250        match variant_val {
251            Value::Variant(variant_data) => {
252                if variant_data.fields.is_empty() {
253                    panic!("variant-init: variant has no fields");
254                }
255
256                // Create new fields without the last element
257                let new_fields: Vec<Value> =
258                    variant_data.fields[..variant_data.fields.len() - 1].to_vec();
259
260                let new_variant =
261                    Value::Variant(Box::new(VariantData::new(variant_data.tag, new_fields)));
262
263                push(stack, new_variant)
264            }
265            _ => panic!("variant-init: expected Variant, got {:?}", variant_val),
266        }
267    }
268}
269
270// Public re-exports with short names for internal use
271pub use patch_seq_make_variant as make_variant;
272pub use patch_seq_variant_append as variant_append;
273pub use patch_seq_variant_field_at as variant_field_at;
274pub use patch_seq_variant_field_count as variant_field_count;
275pub use patch_seq_variant_init as variant_init;
276pub use patch_seq_variant_last as variant_last;
277pub use patch_seq_variant_tag as variant_tag;
278
279#[cfg(test)]
280mod tests {
281    use super::*;
282    use crate::seqstring::global_string;
283    use crate::value::VariantData;
284
285    #[test]
286    fn test_variant_field_count() {
287        unsafe {
288            // Create a variant with 3 fields
289            let variant = Value::Variant(Box::new(VariantData::new(
290                0,
291                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
292            )));
293
294            let stack = std::ptr::null_mut();
295            let stack = push(stack, variant);
296            let stack = variant_field_count(stack);
297
298            let (stack, result) = pop(stack);
299            assert_eq!(result, Value::Int(3));
300            assert!(stack.is_null());
301        }
302    }
303
304    #[test]
305    fn test_variant_tag() {
306        unsafe {
307            // Create a variant with tag 42
308            let variant = Value::Variant(Box::new(VariantData::new(42, vec![Value::Int(10)])));
309
310            let stack = std::ptr::null_mut();
311            let stack = push(stack, variant);
312            let stack = variant_tag(stack);
313
314            let (stack, result) = pop(stack);
315            assert_eq!(result, Value::Int(42));
316            assert!(stack.is_null());
317        }
318    }
319
320    #[test]
321    fn test_variant_field_at() {
322        unsafe {
323            let str1 = global_string("hello".to_string());
324            let str2 = global_string("world".to_string());
325
326            // Create a variant with mixed fields
327            let variant = Value::Variant(Box::new(VariantData::new(
328                0,
329                vec![
330                    Value::String(str1.clone()),
331                    Value::Int(42),
332                    Value::String(str2.clone()),
333                ],
334            )));
335
336            // Test accessing field 0
337            let stack = std::ptr::null_mut();
338            let stack = push(stack, variant.clone());
339            let stack = push(stack, Value::Int(0));
340            let stack = variant_field_at(stack);
341
342            let (stack, result) = pop(stack);
343            assert_eq!(result, Value::String(str1.clone()));
344            assert!(stack.is_null());
345
346            // Test accessing field 1
347            let stack = push(stack, variant.clone());
348            let stack = push(stack, Value::Int(1));
349            let stack = variant_field_at(stack);
350
351            let (stack, result) = pop(stack);
352            assert_eq!(result, Value::Int(42));
353            assert!(stack.is_null());
354
355            // Test accessing field 2
356            let stack = push(stack, variant.clone());
357            let stack = push(stack, Value::Int(2));
358            let stack = variant_field_at(stack);
359
360            let (stack, result) = pop(stack);
361            assert_eq!(result, Value::String(str2));
362            assert!(stack.is_null());
363        }
364    }
365
366    #[test]
367    fn test_variant_field_count_empty() {
368        unsafe {
369            // Create a variant with no fields
370            let variant = Value::Variant(Box::new(VariantData::new(0, vec![])));
371
372            let stack = std::ptr::null_mut();
373            let stack = push(stack, variant);
374            let stack = variant_field_count(stack);
375
376            let (stack, result) = pop(stack);
377            assert_eq!(result, Value::Int(0));
378            assert!(stack.is_null());
379        }
380    }
381
382    #[test]
383    fn test_make_variant_with_fields() {
384        unsafe {
385            // Create: 10 20 30 3 42 make-variant
386            // Should produce variant with tag 42 and fields [10, 20, 30]
387            let stack = std::ptr::null_mut();
388            let stack = push(stack, Value::Int(10)); // field 0
389            let stack = push(stack, Value::Int(20)); // field 1
390            let stack = push(stack, Value::Int(30)); // field 2
391            let stack = push(stack, Value::Int(3)); // count
392            let stack = push(stack, Value::Int(42)); // tag
393
394            let stack = make_variant(stack);
395
396            let (stack, result) = pop(stack);
397
398            match result {
399                Value::Variant(v) => {
400                    assert_eq!(v.tag, 42);
401                    assert_eq!(v.fields.len(), 3);
402                    assert_eq!(v.fields[0], Value::Int(10));
403                    assert_eq!(v.fields[1], Value::Int(20));
404                    assert_eq!(v.fields[2], Value::Int(30));
405                }
406                _ => panic!("Expected Variant"),
407            }
408            assert!(stack.is_null());
409        }
410    }
411
412    #[test]
413    fn test_make_variant_empty() {
414        unsafe {
415            // Create: 0 0 make-variant
416            // Should produce variant with tag 0 and no fields
417            let stack = std::ptr::null_mut();
418            let stack = push(stack, Value::Int(0)); // count
419            let stack = push(stack, Value::Int(0)); // tag
420
421            let stack = make_variant(stack);
422
423            let (stack, result) = pop(stack);
424
425            match result {
426                Value::Variant(v) => {
427                    assert_eq!(v.tag, 0);
428                    assert_eq!(v.fields.len(), 0);
429                }
430                _ => panic!("Expected Variant"),
431            }
432            assert!(stack.is_null());
433        }
434    }
435
436    #[test]
437    fn test_make_variant_with_mixed_types() {
438        unsafe {
439            let s = global_string("hello".to_string());
440
441            // Create variant with mixed field types
442            let stack = std::ptr::null_mut();
443            let stack = push(stack, Value::Int(42));
444            let stack = push(stack, Value::String(s.clone()));
445            let stack = push(stack, Value::Float(3.5));
446            let stack = push(stack, Value::Int(3)); // count
447            let stack = push(stack, Value::Int(1)); // tag
448
449            let stack = make_variant(stack);
450
451            let (stack, result) = pop(stack);
452
453            match result {
454                Value::Variant(v) => {
455                    assert_eq!(v.tag, 1);
456                    assert_eq!(v.fields.len(), 3);
457                    assert_eq!(v.fields[0], Value::Int(42));
458                    assert_eq!(v.fields[1], Value::String(s));
459                    assert_eq!(v.fields[2], Value::Float(3.5));
460                }
461                _ => panic!("Expected Variant"),
462            }
463            assert!(stack.is_null());
464        }
465    }
466
467    #[test]
468    fn test_variant_append() {
469        unsafe {
470            // Create an empty variant (tag 4 for array)
471            let stack = std::ptr::null_mut();
472            let stack = push(stack, Value::Int(0)); // count
473            let stack = push(stack, Value::Int(4)); // tag (array)
474            let stack = make_variant(stack);
475
476            // Append a value
477            let stack = push(stack, Value::Int(42));
478            let stack = variant_append(stack);
479
480            // Check result
481            let (stack, result) = pop(stack);
482            match result {
483                Value::Variant(v) => {
484                    assert_eq!(v.tag, 4);
485                    assert_eq!(v.fields.len(), 1);
486                    assert_eq!(v.fields[0], Value::Int(42));
487                }
488                _ => panic!("Expected Variant"),
489            }
490            assert!(stack.is_null());
491        }
492    }
493
494    #[test]
495    fn test_variant_append_multiple() {
496        unsafe {
497            // Create an empty variant (tag 5 for object)
498            let stack = std::ptr::null_mut();
499            let stack = push(stack, Value::Int(0)); // count
500            let stack = push(stack, Value::Int(5)); // tag (object)
501            let stack = make_variant(stack);
502
503            // Append key
504            let key = global_string("name".to_string());
505            let stack = push(stack, Value::String(key.clone()));
506            let stack = variant_append(stack);
507
508            // Append value
509            let val = global_string("John".to_string());
510            let stack = push(stack, Value::String(val.clone()));
511            let stack = variant_append(stack);
512
513            // Check result - should have 2 fields
514            let (stack, result) = pop(stack);
515            match result {
516                Value::Variant(v) => {
517                    assert_eq!(v.tag, 5);
518                    assert_eq!(v.fields.len(), 2);
519                    assert_eq!(v.fields[0], Value::String(key));
520                    assert_eq!(v.fields[1], Value::String(val));
521                }
522                _ => panic!("Expected Variant"),
523            }
524            assert!(stack.is_null());
525        }
526    }
527
528    #[test]
529    fn test_variant_last() {
530        unsafe {
531            // Create a variant with 3 fields
532            let variant = Value::Variant(Box::new(VariantData::new(
533                0,
534                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
535            )));
536
537            let stack = std::ptr::null_mut();
538            let stack = push(stack, variant);
539            let stack = variant_last(stack);
540
541            let (stack, result) = pop(stack);
542            assert_eq!(result, Value::Int(30));
543            assert!(stack.is_null());
544        }
545    }
546
547    #[test]
548    fn test_variant_init() {
549        unsafe {
550            // Create a variant with 3 fields
551            let variant = Value::Variant(Box::new(VariantData::new(
552                42,
553                vec![Value::Int(10), Value::Int(20), Value::Int(30)],
554            )));
555
556            let stack = std::ptr::null_mut();
557            let stack = push(stack, variant);
558            let stack = variant_init(stack);
559
560            let (stack, result) = pop(stack);
561            match result {
562                Value::Variant(v) => {
563                    assert_eq!(v.tag, 42); // tag preserved
564                    assert_eq!(v.fields.len(), 2);
565                    assert_eq!(v.fields[0], Value::Int(10));
566                    assert_eq!(v.fields[1], Value::Int(20));
567                }
568                _ => panic!("Expected Variant"),
569            }
570            assert!(stack.is_null());
571        }
572    }
573
574    #[test]
575    fn test_variant_stack_operations() {
576        // Test using variant as a stack: append, append, last, init, last
577        unsafe {
578            // Create empty "stack" variant (tag 99)
579            let stack = std::ptr::null_mut();
580            let stack = push(stack, Value::Int(0)); // count
581            let stack = push(stack, Value::Int(99)); // tag
582            let stack = make_variant(stack);
583
584            // Append 10
585            let stack = push(stack, Value::Int(10));
586            let stack = variant_append(stack);
587
588            // Append 20
589            let stack = push(stack, Value::Int(20));
590            let stack = variant_append(stack);
591
592            // Now have variant with [10, 20] on stack
593            // Dup and get last (should be 20)
594            let (stack, variant) = pop(stack);
595            let stack = push(stack, variant.clone());
596            let stack = push(stack, variant);
597            let stack = variant_last(stack);
598            let (stack, top) = pop(stack);
599            assert_eq!(top, Value::Int(20));
600
601            // Now use init to remove last element
602            let stack = variant_init(stack);
603
604            // Dup and get last (should now be 10)
605            let (stack, variant) = pop(stack);
606            let stack = push(stack, variant.clone());
607            let stack = push(stack, variant);
608            let stack = variant_last(stack);
609            let (stack, top) = pop(stack);
610            assert_eq!(top, Value::Int(10));
611
612            // Verify remaining variant has 1 field
613            let (stack, result) = pop(stack);
614            match result {
615                Value::Variant(v) => {
616                    assert_eq!(v.fields.len(), 1);
617                    assert_eq!(v.fields[0], Value::Int(10));
618                }
619                _ => panic!("Expected Variant"),
620            }
621            assert!(stack.is_null());
622        }
623    }
624}