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}