sphinx/runtime/types/
tuple.rs1use core::cmp::Ordering;
2use core::fmt::{self, Write};
3use crate::runtime::Variant;
4use crate::runtime::gc::{Gc, GcTrace};
5use crate::runtime::strings::{StringValue, static_symbol};
6use crate::runtime::iter::IterState;
7use crate::runtime::types::{Type, MetaObject, UserIterator};
8use crate::runtime::errors::{ExecResult, RuntimeError};
9
10#[derive(Clone, Copy)]
11pub enum Tuple {
12 Empty,
13 NonEmpty(Gc<[Variant]>),
14}
15
16impl Default for Tuple {
17 fn default() -> Self { Self::Empty }
18}
19
20impl From<Box<[Variant]>> for Tuple {
21 fn from(items: Box<[Variant]>) -> Self {
22 if items.is_empty() {
23 Self::Empty
24 } else {
25 Self::NonEmpty(Gc::from_box(items))
26 }
27 }
28}
29
30impl AsRef<[Variant]> for Tuple {
31 fn as_ref(&self) -> &[Variant] {
32 self.items()
33 }
34}
35
36impl Tuple {
37 pub fn trace(&self) {
38 if let Self::NonEmpty(gc_items) = self {
39 gc_items.mark_trace()
40 }
41 }
42
43 pub fn len(&self) -> usize {
44 match self {
45 Self::Empty => 0,
46 Self::NonEmpty(items) => items.len(),
47 }
48 }
49
50 pub fn is_empty(&self) -> bool {
51 match self {
52 Self::Empty => true,
53 Self::NonEmpty(..) => false,
54 }
55 }
56
57 pub fn items(&self) -> &[Variant] {
58 match self {
59 Self::Empty => &[] as &[Variant],
60 Self::NonEmpty(items) => &**items,
61 }
62 }
63}
64
65
66impl Tuple {
68 fn eq(&self, other: &Self) -> ExecResult<bool> {
69 if self.len() != other.len() {
70 return Ok(false);
71 }
72
73 let pairs = self.items().iter()
74 .zip(other.items().iter());
75
76 for (a, b) in pairs {
77 if !a.cmp_eq(b)? {
78 return Ok(false);
79 }
80 }
81 Ok(true)
82 }
83
84 fn cmp(&self, other: &Self) -> ExecResult<Ordering> {
86 let pairs = self.items().iter()
87 .zip(other.items().iter());
88
89 for (a, b) in pairs {
90 if a.cmp_lt(b)? {
91 return Ok(Ordering::Less)
92 }
93 if !a.cmp_eq(b)? {
94 return Ok(Ordering::Greater)
95 }
96 }
97
98 Ok(self.len().cmp(&other.len()))
101 }
102
103 fn cmp_lt(&self, other: &Self) -> ExecResult<bool> {
104 Ok(self.cmp(other)? == Ordering::Less)
105 }
106
107 fn cmp_le(&self, other: &Self) -> ExecResult<bool> {
108 Ok(matches!(self.cmp(other)?, Ordering::Equal|Ordering::Less))
109 }
110}
111
112impl MetaObject for Tuple {
113 fn type_tag(&self) -> Type { Type::Tuple }
114
115 fn len(&self) -> Option<ExecResult<usize>> {
116 Some(Ok(Tuple::len(self)))
117 }
118
119 fn iter_init(&self) -> Option<ExecResult<IterState>> {
120 let iter: Box<dyn UserIterator> = Box::new(TupleIter(*self));
121 let iter = Gc::from_box(iter);
122 iter.iter_init()
123 }
124
125 fn cmp_eq(&self, other: &Variant) -> Option<ExecResult<bool>> {
126 if let Variant::Tuple(other) = other {
127 return Some(self.eq(other));
128 }
129 None
130 }
131
132 fn cmp_lt(&self, other: &Variant) -> Option<ExecResult<bool>> {
133 if let Variant::Tuple(other) = other {
134 return Some(self.cmp_lt(other));
135 }
136 None
137 }
138
139 fn cmp_le(&self, other: &Variant) -> Option<ExecResult<bool>> {
140 if let Variant::Tuple(other) = other {
141 return Some(self.cmp_le(other));
142 }
143 None
144 }
145
146 fn fmt_repr(&self) -> ExecResult<StringValue> {
147 match self {
148 Self::Empty => Ok(StringValue::from(static_symbol!("()"))),
149
150 Self::NonEmpty(items) => {
151 let (first, rest) = items.split_first().unwrap(); let mut buf = String::new();
154
155 write!(&mut buf, "({}", first.fmt_repr()?)
156 .map_err(|err| RuntimeError::other(err.to_string()))?;
157
158 for item in rest.iter() {
159 write!(&mut buf, ", {}", item.fmt_repr()?)
160 .map_err(|err| RuntimeError::other(err.to_string()))?;
161 }
162 buf.push(')');
163
164
165 Ok(StringValue::new_maybe_interned(buf))
166 }
167 }
168 }
169}
170
171impl fmt::Debug for Tuple {
172 fn fmt(&self, fmt: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
173 let mut tuple = fmt.debug_tuple("");
174 for item in self.items().iter() {
175 tuple.field(item);
176 }
177 tuple.finish()
178 }
179}
180
181#[derive(Debug)]
183struct TupleIter(Tuple);
184
185unsafe impl GcTrace for TupleIter {
186 fn trace(&self) {
187 self.0.trace()
188 }
189}
190
191impl UserIterator for TupleIter {
192 fn get_item(&self, state: &Variant) -> ExecResult<Variant> {
193 let idx = usize::try_from(state.as_int()?)
194 .map_err(|_| RuntimeError::invalid_value("invalid state"))?;
195
196 let items = self.0.items();
197 Ok(items[idx])
198 }
199
200 fn next_state(&self, state: Option<&Variant>) -> ExecResult<Variant> {
201 let next = match state {
202 Some(state) => state.as_int()?
203 .checked_add(1)
204 .ok_or(RuntimeError::overflow_error())?,
205
206 None => 0,
207 };
208
209 let next_idx = usize::try_from(next)
210 .map_err(|_| RuntimeError::invalid_value("invalid state"))?;
211
212 if next_idx >= self.0.len() {
213 return Ok(Variant::Nil)
214 }
215 Ok(Variant::from(next))
216 }
217}