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