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}