Skip to main content

seq_runtime/list_ops/
basic.rs

1//! Basic list operations: `list_length`, `list_empty`, `list_make`,
2//! `list_push`, `list_reverse`.
3
4use crate::stack::{Stack, peek_heap_mut_second, pop, push};
5use crate::value::{Value, VariantData};
6use std::sync::Arc;
7
8/// # Safety
9/// Stack must have the expected values on top for this operation.
10#[unsafe(no_mangle)]
11pub unsafe extern "C" fn patch_seq_list_length(stack: Stack) -> Stack {
12    unsafe { crate::variant_ops::patch_seq_variant_field_count(stack) }
13}
14
15/// Check if a list is empty
16///
17/// Stack effect: ( Variant -- Bool )
18///
19/// Returns true if the list has no elements, false otherwise.
20///
21/// # Safety
22/// Stack must have a Variant on top
23#[unsafe(no_mangle)]
24pub unsafe extern "C" fn patch_seq_list_empty(stack: Stack) -> Stack {
25    unsafe {
26        let (stack, list_val) = pop(stack);
27
28        let is_empty = match list_val {
29            Value::Variant(v) => v.fields.is_empty(),
30            _ => panic!("list-empty?: expected Variant (list), got {:?}", list_val),
31        };
32
33        push(stack, Value::Bool(is_empty))
34    }
35}
36
37/// Create an empty list
38///
39/// Stack effect: ( -- Variant )
40///
41/// Returns a new empty list (Variant with tag "List" and no fields).
42///
43/// # Safety
44/// No requirements on stack
45#[unsafe(no_mangle)]
46pub unsafe extern "C" fn patch_seq_list_make(stack: Stack) -> Stack {
47    unsafe {
48        let list = Value::Variant(Arc::new(VariantData::new(
49            crate::seqstring::global_string("List".to_string()),
50            vec![],
51        )));
52        push(stack, list)
53    }
54}
55
56/// Append an element to a list with COW optimization.
57///
58/// Stack effect: ( Variant Value -- Variant )
59///
60/// Fast path: if the list (at sp-2) is a sole-owned heap value, mutates
61/// in place via `peek_heap_mut_second` — no Arc alloc/dealloc cycle.
62/// Slow path: pops, clones, and pushes a new list.
63///
64/// # Safety
65/// Stack must have a Value on top and a Variant (list) below
66#[unsafe(no_mangle)]
67pub unsafe extern "C" fn patch_seq_list_push(stack: Stack) -> Stack {
68    unsafe {
69        // Try the fast path: peek at the list without popping.
70        // SAFETY: list.push requires two values on the stack (enforced by
71        // the type checker), so stack.sub(2) is valid.
72        if let Some(Value::Variant(variant_arc)) = peek_heap_mut_second(stack)
73            && let Some(data) = Arc::get_mut(variant_arc)
74        {
75            // Sole owner all the way down — mutate in place.
76            // Safety: `data` points into the Value at sp-2. `pop` only
77            // touches sp-1 (decrements sp, reads that slot), so sp-2's
78            // memory is not accessed or invalidated by the pop.
79            let (stack, value) = pop(stack);
80            data.fields.push(value);
81            return stack; // List is still at sp-1, untouched
82        }
83
84        // Slow path: pop both, clone if shared, push result
85        let (stack, value) = pop(stack);
86        let (stack, list_val) = pop(stack);
87        let variant_arc = match list_val {
88            Value::Variant(v) => v,
89            _ => panic!("list.push: expected Variant (list), got {:?}", list_val),
90        };
91        push_to_variant(stack, variant_arc, value)
92    }
93}
94
95/// COW push helper: append value to variant, mutating in place when sole owner.
96unsafe fn push_to_variant(stack: Stack, mut variant_arc: Arc<VariantData>, value: Value) -> Stack {
97    unsafe {
98        if let Some(data) = Arc::get_mut(&mut variant_arc) {
99            // Sole owner — mutate in place (amortized O(1))
100            data.fields.push(value);
101            push(stack, Value::Variant(variant_arc))
102        } else {
103            // Shared — clone and append (O(n))
104            let mut new_fields = Vec::with_capacity(variant_arc.fields.len() + 1);
105            new_fields.extend(variant_arc.fields.iter().cloned());
106            new_fields.push(value);
107            let new_list = Value::Variant(Arc::new(VariantData::new(
108                variant_arc.tag.clone(),
109                new_fields,
110            )));
111            push(stack, new_list)
112        }
113    }
114}
115
116/// Get an element from a list by index
117///
118/// Stack effect: ( Variant Int -- Value Bool )
119///
120/// Returns the value at the given index and true, or
121/// a placeholder value and false if index is out of bounds.
122///
123/// # Error Handling
124/// - Empty stack: Sets runtime error, returns 0 and false
125/// - Type mismatch: Sets runtime error, returns 0 and false
126/// - Out of bounds: Returns 0 and false (no error set, this is expected)
127///
128/// # Safety
129/// Stack must have an Int on top and a Variant (list) below
130#[unsafe(no_mangle)]
131pub unsafe extern "C" fn patch_seq_list_reverse(stack: Stack) -> Stack {
132    unsafe {
133        let (stack, list_val) = pop(stack);
134
135        let variant_data = match list_val {
136            Value::Variant(v) => v,
137            _ => panic!("list.reverse: expected Variant (list), got {:?}", list_val),
138        };
139
140        let mut reversed: Vec<Value> = variant_data.fields.to_vec();
141        reversed.reverse();
142
143        let new_variant = Value::Variant(Arc::new(VariantData::new(
144            variant_data.tag.clone(),
145            reversed,
146        )));
147
148        push(stack, new_variant)
149    }
150}