onion_vm/types/
tuple.rs

1use std::{collections::VecDeque, fmt::Debug};
2
3use arc_gc::{
4    arc::{GCArc, GCArcWeak},
5    traceable::GCTraceable,
6};
7
8use crate::lambda::runnable::RuntimeError;
9
10use super::object::{OnionObject, OnionObjectCell, OnionStaticObject};
11
12pub struct OnionTuple {
13    elements: Vec<OnionObject>,
14}
15
16impl GCTraceable<OnionObjectCell> for OnionTuple {
17    fn collect(&self, queue: &mut VecDeque<GCArcWeak<OnionObjectCell>>) {
18        for element in &self.elements {
19            element.collect(queue);
20        }
21    }
22}
23
24impl Debug for OnionTuple {
25    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
26        match self.elements.len() {
27            0 => write!(f, "()"),
28            1 => write!(f, "({:?},)", self.elements[0]),
29            _ => {
30                let elements: Vec<String> =
31                    self.elements.iter().map(|e| format!("{:?}", e)).collect();
32                write!(f, "({})", elements.join(", "))
33            }
34        }
35    }
36}
37
38#[macro_export]
39macro_rules! onion_tuple {
40    ($($x:expr),*) => {
41        OnionTuple::new_static(vec![$($x),*])
42    };
43    () => {
44
45    };
46}
47
48impl OnionTuple {
49    pub fn new(elements: Vec<OnionObject>) -> Self {
50        OnionTuple {
51            elements: elements.into(),
52        }
53    }
54
55    pub fn new_static(elements: Vec<&OnionStaticObject>) -> OnionStaticObject {
56        OnionStaticObject::new(OnionObject::Tuple(
57            OnionTuple {
58                elements: elements
59                    .into_iter()
60                    .map(|e| e.weak().clone())
61                    .collect::<Vec<_>>(),
62            }
63            .into(),
64        ))
65    }
66
67    pub fn new_static_no_ref(elements: &Vec<OnionStaticObject>) -> OnionStaticObject {
68        OnionObject::Tuple(
69            OnionTuple {
70                elements: elements
71                    .into_iter()
72                    .map(|e| e.weak().clone())
73                    .collect::<Vec<_>>(),
74            }
75            .into(),
76        )
77        .consume_and_stabilize()
78    }
79
80    #[inline(always)]
81    pub fn get_elements(&self) -> &Vec<OnionObject> {
82        &self.elements
83    }
84
85    pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
86        self.elements.iter().for_each(|e| e.upgrade(collected));
87    }
88
89    pub fn len(&self) -> Result<OnionStaticObject, RuntimeError> {
90        Ok(OnionStaticObject::new(OnionObject::Integer(
91            self.elements.len() as i64,
92        )))
93    }
94
95    pub fn at(&self, index: i64) -> Result<OnionStaticObject, RuntimeError> {
96        if index < 0 || index >= self.elements.len() as i64 {
97            return Err(RuntimeError::InvalidOperation(
98                format!("Index out of bounds: {}", index).into(),
99            ));
100        }
101        Ok(OnionStaticObject::new(
102            self.elements[index as usize].clone(),
103        ))
104    }
105
106    pub fn with_index<F, R>(&self, index: i64, f: &F) -> Result<R, RuntimeError>
107    where
108        F: Fn(&OnionObject) -> Result<R, RuntimeError>,
109    {
110        if index < 0 || index >= self.elements.len() as i64 {
111            return Err(RuntimeError::InvalidOperation(
112                format!("Index out of bounds: {}", index).into(),
113            ));
114        }
115        let borrowed = &self.elements[index as usize];
116        f(borrowed)
117    }
118
119    pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
120    where
121        F: Fn(&OnionObject) -> Result<R, RuntimeError>,
122    {
123        for element in &self.elements {
124            match element {
125                OnionObject::Named(named) => {
126                    if named.get_key().equals(key)? {
127                        return f(&named.get_value());
128                    }
129                }
130                OnionObject::Pair(pair) => {
131                    if pair.get_key().equals(key)? {
132                        return f(&pair.get_value());
133                    }
134                }
135                _ => {}
136            }
137        }
138        Err(RuntimeError::InvalidOperation(
139            format!("Attribute {:?} not found in tuple", key).into(),
140        ))
141    }
142
143    pub fn binary_add(&self, other: &OnionObject) -> Result<OnionStaticObject, RuntimeError> {
144        match other {
145            OnionObject::Tuple(other_tuple) => {
146                let mut new_elements = self.elements.clone();
147                new_elements.extend(other_tuple.elements.clone());
148                Ok(OnionStaticObject::new(OnionObject::Tuple(
149                    OnionTuple {
150                        elements: new_elements,
151                    }
152                    .into(),
153                )))
154            }
155            _ => Ok(OnionStaticObject::new(OnionObject::Undefined(Some(
156                format!("Cannot add tuple with {:?}", other).into(),
157            )))),
158        }
159    }
160
161    pub fn contains(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
162        for element in &self.elements {
163            if element.equals(other)? {
164                return Ok(true);
165            }
166        }
167        Ok(false)
168    }
169}
170
171impl OnionTuple {
172    pub fn equals(&self, other: &OnionObject) -> Result<bool, RuntimeError> {
173        match other {
174            OnionObject::Tuple(other_tuple) => {
175                if self.elements.len() != other_tuple.elements.len() {
176                    return Ok(false);
177                }
178                for (a, b) in self.elements.iter().zip(&other_tuple.elements) {
179                    if a.equals(b)? {
180                        return Ok(false);
181                    }
182                }
183                Ok(true)
184            }
185            _ => Ok(false),
186        }
187    }
188}
189
190impl OnionTuple {
191    pub fn reconstruct_container(&self) -> Result<OnionObject, RuntimeError> {
192        let mut cloned_elements = Vec::with_capacity(self.elements.len());
193        for element in &self.elements {
194            cloned_elements.push(element.clone());
195        }
196        Ok(OnionObject::Tuple(
197            OnionTuple {
198                elements: cloned_elements,
199            }
200            .into(),
201        ))
202    }
203}