sphinx/runtime/types/
tuple.rs

1use 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
66// helpers
67impl 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    // compare two tuple lexicographically
85    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        // if we get here then all tested elements were equal
99        // in which case the longer tuple is considered greater
100        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(); // should never be empty
152                
153                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// Tuple Iterator
182#[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}