1use crate::ExprType;
20use crate::num::{U24, unchecked_u24_as_usize};
21use std::convert::TryFrom;
22use std::hash::Hash;
23
24#[derive(Clone, Debug)]
26pub(crate) struct ArrayData {
27 pub(crate) dimensions: Vec<usize>,
29
30 pub(crate) values: Vec<u64>,
32}
33
34impl ArrayData {
35 pub(crate) fn flat_index(&self, subscripts: &[i32]) -> Result<usize, String> {
37 debug_assert_eq!(
38 subscripts.len(),
39 self.dimensions.len(),
40 "Invalid number of subscripts; guaranteed valid by the compiler"
41 );
42
43 let mut offset = 0;
44 let mut multiplier = 1;
45 for (s, d) in subscripts.iter().zip(&self.dimensions) {
46 let Ok(s) = usize::try_from(*s) else {
47 return Err(format!("Subscript {} cannot be negative", s));
48 };
49 if s >= *d {
50 return Err(format!("Subscript {} exceeds limit of {}", s, d));
51 }
52 offset += s * multiplier;
53 multiplier *= d;
54 }
55 Ok(offset)
56 }
57}
58
59#[derive(Clone, Debug)]
65pub enum ConstantDatum {
66 Boolean(bool),
68
69 Double(f64),
71
72 Integer(i32),
74
75 Text(String),
77}
78
79impl PartialEq for ConstantDatum {
80 fn eq(&self, other: &Self) -> bool {
81 match (self, other) {
82 (Self::Boolean(a), Self::Boolean(b)) => a == b,
83 (Self::Double(a), Self::Double(b)) => a.to_bits() == b.to_bits(),
84 (Self::Integer(a), Self::Integer(b)) => a == b,
85 (Self::Text(a), Self::Text(b)) => a == b,
86 _ => false,
87 }
88 }
89}
90
91impl Eq for ConstantDatum {}
92
93impl Hash for ConstantDatum {
94 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
95 std::mem::discriminant(self).hash(state);
96 match self {
97 Self::Boolean(b) => b.hash(state),
98 Self::Double(d) => d.to_bits().hash(state),
99 Self::Integer(i) => i.hash(state),
100 Self::Text(s) => s.hash(state),
101 }
102 }
103}
104
105impl From<bool> for ConstantDatum {
106 fn from(value: bool) -> Self {
107 Self::Boolean(value)
108 }
109}
110
111impl From<f64> for ConstantDatum {
112 fn from(value: f64) -> Self {
113 Self::Double(value)
114 }
115}
116
117impl From<i32> for ConstantDatum {
118 fn from(value: i32) -> Self {
119 Self::Integer(value)
120 }
121}
122
123impl From<&str> for ConstantDatum {
124 fn from(value: &str) -> Self {
125 Self::Text(value.to_owned())
126 }
127}
128
129impl From<String> for ConstantDatum {
130 fn from(value: String) -> Self {
131 Self::Text(value)
132 }
133}
134
135impl ConstantDatum {
136 pub(crate) fn etype(&self) -> ExprType {
138 match self {
139 Self::Boolean(..) => ExprType::Boolean,
140 Self::Double(..) => ExprType::Double,
141 Self::Integer(..) => ExprType::Integer,
142 Self::Text(..) => ExprType::Text,
143 }
144 }
145
146 pub fn as_source(&self) -> String {
148 match self {
149 Self::Boolean(v) => {
150 if *v {
151 "TRUE".to_owned()
152 } else {
153 "FALSE".to_owned()
154 }
155 }
156 Self::Double(v) => {
157 let mut s = format!("{}", v);
158 if !s.contains('.') && !s.contains('e') && !s.contains('E') {
159 s.push_str(".0");
160 }
161 s
162 }
163 Self::Integer(v) => format!("{}", v),
164 Self::Text(v) => format!("\"{}\"", v.replace('"', "\"\"")),
165 }
166 }
167
168 pub(crate) fn as_disassembly(&self) -> String {
170 match self {
171 Self::Boolean(v) => {
172 if *v {
173 "TRUE".to_owned()
174 } else {
175 "FALSE".to_owned()
176 }
177 }
178 Self::Double(v) => v.to_string(),
179 Self::Integer(v) => format!("{}", v),
180 Self::Text(v) => format!("\"{}\"", v.replace('"', "\"\"")),
181 }
182 }
183
184 pub(crate) fn from_raw(
186 value: u64,
187 etype: ExprType,
188 constants: &[ConstantDatum],
189 heap: &Heap,
190 ) -> Self {
191 match etype {
192 ExprType::Boolean => Self::Boolean(value != 0),
193 ExprType::Double => Self::Double(f64::from_bits(value)),
194 ExprType::Integer => Self::Integer(value as i32),
195 ExprType::Text => {
196 let ptr = DatumPtr::from(value);
197 Self::Text(ptr.resolve_string(constants, heap).to_owned())
198 }
199 }
200 }
201}
202
203#[derive(Clone, Debug)]
208pub(crate) enum HeapDatum {
209 Array(ArrayData),
211
212 Text(String),
214}
215
216#[derive(Debug, thiserror::Error)]
218#[error("Out of heap space")]
219pub(crate) struct HeapOverflowError;
220
221pub(crate) struct Heap {
223 data: Vec<HeapDatum>,
224 max_entries: U24,
225}
226
227impl Heap {
228 pub(crate) fn new(max_entries: U24) -> Self {
230 Self { data: vec![HeapDatum::Text(String::new())], max_entries }
231 }
232
233 pub(crate) fn clear(&mut self) {
235 *self = Self::new(self.max_entries);
236 }
237
238 pub(crate) fn len(&self) -> usize {
240 self.data.len()
241 }
242
243 pub(crate) fn empty_text_ptr(&self) -> u64 {
245 debug_assert!(matches!(self.data.first(), Some(HeapDatum::Text(s)) if s.is_empty()));
246 DatumPtr::for_heap(0)
247 }
248
249 pub(crate) fn push(&mut self, datum: HeapDatum) -> Result<u64, HeapOverflowError> {
251 if matches!(&datum, HeapDatum::Text(s) if s.is_empty()) {
252 return Ok(self.empty_text_ptr());
253 }
254
255 let index = U24::try_from(self.len()).map_err(|_| HeapOverflowError)?;
256 if self.len() >= unchecked_u24_as_usize(self.max_entries) {
257 return Err(HeapOverflowError);
258 }
259 self.data.push(datum);
260 Ok(DatumPtr::for_heap(u32::from(index)))
261 }
262
263 pub(crate) fn get(&self, index: usize) -> &HeapDatum {
265 &self.data[index]
266 }
267
268 pub(crate) fn get_mut(&mut self, index: usize) -> &mut HeapDatum {
270 &mut self.data[index]
271 }
272}
273
274#[derive(Clone, Copy)]
283pub(crate) enum DatumPtr {
284 Constant(U24),
286
287 Heap(U24),
289}
290
291impl From<u64> for DatumPtr {
292 fn from(value: u64) -> Self {
293 let signed_value = value as i32;
294 if signed_value < 0 {
295 DatumPtr::Heap(U24::try_from((-signed_value - 1) as u32).unwrap())
296 } else {
297 DatumPtr::Constant(U24::try_from(signed_value as u32).unwrap())
298 }
299 }
300}
301
302impl DatumPtr {
303 pub(crate) fn for_heap(index: u32) -> u64 {
305 let raw = index as i32;
306 let raw = -raw - 1;
307 raw as u64
308 }
309
310 pub(crate) fn resolve_string<'b>(
314 &self,
315 constants: &'b [ConstantDatum],
316 heap: &'b Heap,
317 ) -> &'b str {
318 match self {
319 DatumPtr::Constant(index) => match &constants[unchecked_u24_as_usize(*index)] {
320 ConstantDatum::Text(s) => s,
321 _ => panic!("Constant pointer does not point to a Text value"),
322 },
323 DatumPtr::Heap(index) => match heap.get(unchecked_u24_as_usize(*index)) {
324 HeapDatum::Text(s) => s,
325 _ => panic!("Heap pointer does not point to a Text value"),
326 },
327 }
328 }
329
330 pub(crate) fn heap_index(&self) -> usize {
334 match self {
335 DatumPtr::Heap(index) => unchecked_u24_as_usize(*index),
336 DatumPtr::Constant(_) => panic!("Expected a heap pointer"),
337 }
338 }
339}
340
341#[cfg(test)]
342mod tests {
343 use super::*;
344
345 #[test]
346 fn test_constant_datum_from_scalars() {
347 assert_eq!(ConstantDatum::Boolean(true), ConstantDatum::from(true));
348 assert_eq!(ConstantDatum::Double(3.25), ConstantDatum::from(3.25));
349 assert_eq!(ConstantDatum::Integer(-7), ConstantDatum::from(-7));
350 }
351
352 #[test]
353 fn test_constant_datum_from_str() {
354 let mut text = "hello".to_owned();
355 let datum = ConstantDatum::from(text.as_str());
356 text.clear();
357
358 assert_eq!(ConstantDatum::Text("hello".to_owned()), datum);
359 }
360
361 #[test]
362 fn test_constant_datum_from_string() {
363 assert_eq!(
364 ConstantDatum::Text("hello".to_owned()),
365 ConstantDatum::from("hello".to_owned())
366 );
367 }
368
369 #[test]
370 fn test_constant_datum_from_raw() {
371 assert_eq!(
372 ConstantDatum::Boolean(true),
373 ConstantDatum::from_raw(1, ExprType::Boolean, &[], &Heap::new(U24::from(64)))
374 );
375 assert_eq!(
376 ConstantDatum::Double(3.25),
377 ConstantDatum::from_raw(
378 3.25f64.to_bits(),
379 ExprType::Double,
380 &[],
381 &Heap::new(U24::from(64))
382 )
383 );
384 assert_eq!(
385 ConstantDatum::Integer(-7),
386 ConstantDatum::from_raw(
387 (-7i32) as u64,
388 ExprType::Integer,
389 &[],
390 &Heap::new(U24::from(64))
391 )
392 );
393
394 let constants = vec![ConstantDatum::Text("hello".to_owned())];
395 assert_eq!(
396 ConstantDatum::Text("hello".to_owned()),
397 ConstantDatum::from_raw(0, ExprType::Text, &constants, &Heap::new(U24::from(64)))
398 );
399 }
400}