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: Box<[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                    .into_boxed_slice(),
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                    .into_boxed_slice(),
76            }
77            .into(),
78        )
79        .consume_and_stabilize()
80    }
81
82    #[inline(always)]
83    pub fn get_elements(&self) -> &Box<[OnionObject]> {
84        &self.elements
85    }
86
87    pub fn upgrade(&self, collected: &mut Vec<GCArc<OnionObjectCell>>) {
88        self.elements.iter().for_each(|e| e.upgrade(collected));
89    }
90
91    pub fn len(&self) -> Result<OnionStaticObject, RuntimeError> {
92        Ok(OnionStaticObject::new(OnionObject::Integer(
93            self.elements.len() as i64,
94        )))
95    }
96
97    pub fn at(&self, index: i64) -> Result<OnionStaticObject, RuntimeError> {
98        if index < 0 || index >= self.elements.len() as i64 {
99            return Err(RuntimeError::InvalidOperation(
100                format!("Index out of bounds: {}", index).into(),
101            ));
102        }
103        Ok(OnionStaticObject::new(
104            self.elements[index as usize].clone(),
105        ))
106    }
107
108    pub fn with_index<F, R>(&self, index: i64, f: &F) -> Result<R, RuntimeError>
109    where
110        F: Fn(&OnionObject) -> Result<R, RuntimeError>,
111    {
112        if index < 0 || index >= self.elements.len() as i64 {
113            return Err(RuntimeError::InvalidOperation(
114                format!("Index out of bounds: {}", index).into(),
115            ));
116        }
117        let borrowed = &self.elements[index as usize];
118        f(borrowed)
119    }
120
121    pub fn with_attribute<F, R>(&self, key: &OnionObject, f: &F) -> Result<R, RuntimeError>
122    where
123        F: Fn(&OnionObject) -> Result<R, RuntimeError>,
124    {
125        for element in &self.elements {
126            match element {
127                OnionObject::Pair(pair) => {
128                    if pair.get_key().equals(key)? {
129                        return f(&pair.get_value());
130                    }
131                }
132                _ => {}
133            }
134        }
135        Err(RuntimeError::InvalidOperation(
136            format!("Attribute {:?} not found in tuple", key).into(),
137        ))
138    }
139
140    pub fn binary_add(&self, other: &OnionObject) -> Result<OnionStaticObject, RuntimeError> {
141        match other {
142            OnionObject::Tuple(other_tuple) => {
143                let new_elements: Box<[OnionObject]> = self
144                    .elements
145                    .iter()
146                    .chain(other_tuple.elements.iter())
147                    .cloned()
148                    .collect();
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}