synthphonia/
value.rs

1use derive_more::DebugCustom;
2use derive_more::Display;
3use derive_more::TryInto;
4use derive_more::From;
5use itertools::Itertools;
6
7use crate::expr::Expr;
8use crate::galloc::AllocForExactSizeIter;
9use crate::galloc::AllocForIter;
10use crate::tree_learning::bits::BoxSliceExt;
11use crate::tree_learning::Bits;
12use crate::utils::F64;
13
14
15#[derive(DebugCustom, PartialEq, Eq, Clone, Copy, Hash)]
16/// Represents a comprehensive set of distinct type variants including basic and list-based types. 
17/// 
18/// 
19/// Defines available kinds such as null, integer, boolean, string, float, and their corresponding list forms for integers and strings, with each variant accompanied by custom formatting annotations intended for debugging and display purposes.
20pub enum Type {
21    #[debug(fmt = "Null")]
22    Null,
23    #[debug(fmt = "Int")]
24    Int,
25    #[debug(fmt = "Bool")]
26    Bool,
27    #[debug(fmt = "String")]
28    Str,
29    #[debug(fmt = "Float")]
30    Float,
31    #[debug(fmt = "(List Int)")]
32    ListInt,
33    #[debug(fmt = "(List String)")]
34    ListStr,
35    #[debug(fmt = "(_ BitVec {_0})")]
36    BitVector(usize),
37}
38
39impl Type {
40    /// Returns the corresponding basic type by extracting the underlying non-list variant from a list type, or leaves the type unchanged if it is already basic. 
41    /// 
42    /// 
43    /// Evaluates the input type and, if it represents a list of integers or a list of strings, returns the respective integer or string type; otherwise, it returns the original value.
44    pub fn basic(self) -> Type {
45        match self {
46            Type::ListInt => Self::Int,
47            Type::ListStr => Self::Str,
48            a => a,
49        }
50    }
51    /// Converts a basic type into its corresponding list variant when applicable. 
52    /// 
53    /// 
54    /// Returns an optional list type; if the input is a primitive integer or string type, it wraps the corresponding list type inside a Some, otherwise it returns None.
55    pub fn to_list(self) -> Option<Type> {
56        match self {
57            Type::Int => Some(Type::ListInt),
58            Type::Str => Some(Type::ListStr),
59            _ => None
60        }
61    }
62}
63
64#[derive(DebugCustom, Clone, TryInto, Copy, PartialEq, Eq, Hash, From)]
65/// A collection of constant values representing various primitive and collection types. 
66/// 
67/// This enumeration encapsulates integers, floats, booleans, and strings as well as lists of integers and strings, with each variant storing its data as a static slice to ensure efficient access. 
68/// Additionally, a null variant is provided to denote the absence of a value.
69/// 
70pub enum Value {
71    #[debug(fmt = "{:?}", _0)]
72    Int(&'static [i64]),
73    #[debug(fmt = "{:?}", _0)]
74    Float(&'static [F64]),
75    #[debug(fmt = "{:?}", _0)]
76    Bool(&'static [bool]),
77    #[debug(fmt = "{:?}", _0)]
78    Str(&'static [&'static str]),
79    #[debug(fmt = "{:?}", _0)]
80    ListInt(&'static [&'static [i64]]),
81    #[debug(fmt = "{:?}", _0)]
82    ListStr(&'static [&'static [&'static str]]),
83    #[debug(fmt = "{:x?}", _1)]
84    BitVector(usize, &'static [u64]),
85    #[debug(fmt = "null")]
86    Null,
87}
88
89impl Value {
90
91    /// Transforms the current value by selecting elements at indices specified in the examples slice and produces a new value of the same variant. 
92    /// 
93    /// 
94    /// Iterates over the elements in the internal collection corresponding to the variant and constructs a new value by extracting items at the given indices. 
95    /// If the variant is Null, the result is also Null.
96    pub fn with_examples(self, exs: &[usize]) -> Value {
97        match self {
98            Value::Int(a) => Value::Int(exs.iter().cloned().map(|i| a[i]).galloc_scollect()),
99            Value::Float(a) => Value::Float(exs.iter().cloned().map(|i| a[i]).galloc_scollect()),
100            Value::Bool(a) => Value::Bool(exs.iter().cloned().map(|i| a[i]).galloc_scollect()),
101            Value::Str(a) => Value::Str(exs.iter().cloned().map(|i| a[i]).galloc_scollect()),
102            Value::ListInt(a) => Value::ListInt(exs.iter().cloned().map(|i| a[i]).galloc_scollect()),
103            Value::ListStr(a) => Value::ListStr(exs.iter().cloned().map(|i| a[i]).galloc_scollect()),
104            Value::BitVector(i, a) => Value::BitVector(i, exs.iter().cloned().map(|j| a[j]).galloc_scollect()),
105            Value::Null => Value::Null,
106        }
107    }
108}
109
110impl Value {
111    /// Returns the type corresponding to the variant of the value. 
112    /// This function examines the value's variant and returns the associated type, ensuring that each kind of value consistently maps to its specific type.
113    pub fn ty(&self) -> Type {
114        match self {
115            Self::Int(_) => Type::Int,
116            Self::Bool(_) => Type::Bool,
117            Self::Str(_) => Type::Str,
118            Self::Float(_) => Type::Float,
119            Self::ListInt(_) => Type::ListInt,
120            Self::ListStr(_) => Type::ListStr,
121            Self::BitVector(i, _) => Type::BitVector(*i),
122            Self::Null => Type::Null,
123        }
124    }
125    #[inline(always)]
126    /// Returns the number of elements contained within the value. 
127    /// 
128    /// 
129    /// Examines the variant of the value and computes its length accordingly, using the inherent length of the underlying slice or collection, with a length of zero returned for the null variant.
130    pub fn len(&self) -> usize {
131        match self {
132            Value::Int(a) => a.len(),
133            Value::Bool(b) => b.len(),
134            Value::Str(s) => s.len(),
135            Value::Float(s) => s.len(),
136            Value::ListInt(l) => l.len(),
137            Value::ListStr(l) => l.len(),
138            Value::BitVector(_, b) => b.len(),
139            Value::Null => 0,
140        }
141    }
142    #[inline(always)]
143    /// Returns an optional vector of element lengths contained within the value. 
144    /// This method computes and returns a vector of lengths for each element when applicable, such as when the value holds strings or lists; for other cases, it yields None, indicating that individual element lengths are not defined.
145    pub fn length_inside(&self) -> Option<Vec<usize>> {
146        match self {
147            Value::Int(a) => None,
148            Value::Bool(b) => None,
149            Value::Float(s) => None,
150            Value::Null => None,
151            Value::Str(s) => Some(s.iter().map(|x| x.len()).collect_vec()),
152            Value::ListInt(l) => Some(l.iter().map(|x| x.len()).collect_vec()),
153            Value::ListStr(l) => Some(l.iter().map(|x| x.len()).collect_vec()),
154            Value::BitVector(_, _) => None,
155        }
156    }
157    #[inline(always)]
158    /// Flattens the contained string(s) into a unified static slice of string references. 
159    /// 
160    /// This function operates by taking a value holding either a singular string or a list of strings and returns a reference to a static slice where each element corresponds to a single-character substring from the original strings. 
161    /// It panics if the value is of any other type, ensuring that only supported string types are processed.
162    /// 
163    pub fn flatten_leak(&self) -> &'static [&'static str] {
164        // Memory Leak !!!
165        match self {
166            Value::Str(s) => s.iter().flat_map(|x| (0..x.len()).map(|i| &x[i..i+1]) ).galloc_collect(),
167            Value::ListStr(l) => l.iter().flat_map(|x| x.iter().copied()).galloc_collect(),
168            _ => panic!("Mismatched type: to_liststr_leak")
169        }
170    }
171    #[inline(always)]
172    /// Converts a value holding strings into an optional flattened representation as a static slice of string slices. 
173    /// 
174    /// 
175    /// Checks if the input value encapsulates either individual strings or a list of strings and produces a flattened collection where each element represents a single-character string slice or an element from the list, respectively. 
176    /// If the value does not match these string types, it returns None.
177    pub fn try_flatten_leak(&self) -> Option<&'static [&'static str]> {
178        // Memory Leak !!!
179        match self {
180            Value::Str(s) => Some(s.iter().flat_map(|x| (0..x.len()).map(|i| &x[i..i+1]) ).galloc_collect()),
181            Value::ListStr(l) => Some(l.iter().flat_map(|x| x.iter().copied()).galloc_collect()),
182            _ => None,
183        }
184    }
185
186    /// Creates a synthesized value from an iterator of constant values based on the specified type. 
187    /// 
188    /// 
189    /// Converts each constant from the provided iterator to the corresponding native variant by mapping through type-specific conversion methods and collecting the results into the proper aggregated structure. 
190    /// If the type is unsupported for such conversion, the function triggers a panic with an appropriate error message.
191    pub fn from_const(ty: Type, constants: impl ExactSizeIterator<Item=ConstValue>) -> Self {
192        match ty {
193            Type::Bool => Value::Bool(constants.map(|p| p.as_bool().unwrap()).galloc_scollect()),
194            Type::Int => Value::Int(constants.map(|p| p.as_i64().unwrap()).galloc_scollect()),
195            Type::Str => Value::Str(constants.map(|p| p.as_str().unwrap()).galloc_scollect()),
196            Type::Float => Value::Float(constants.map(|p| p.as_float().unwrap()).galloc_scollect()),
197            Type::BitVector(i) => Value::BitVector(i, constants.map(|p| p.as_bv().unwrap()).galloc_scollect()),
198            _ => panic!("should not reach here"),
199        }
200    }
201    /// Checks whether every string element in the first value is a substring of the corresponding string element in the second value.
202    /// 
203    /// Operates by iterating over paired elements from two string collections and returning true only if each element of the first collection is contained within the corresponding element of the second; if either value is not a string collection, it returns false.
204    pub fn substr(&self, other: &Value) -> bool{
205        match (self, other) {
206            (Value::Str(s), Value::Str(o)) => s.iter().zip(o.iter()).all(|(a,b)| b.contains(a)),
207            _ => false,
208        }
209    }
210    /// Checks whether any string in the first value appears as a substring in the corresponding string of the second value. 
211    /// This function compares two values and, when both are collections of strings, pairs each corresponding element using a zipper technique and returns true if at least one pair satisfies the substring condition; in all other cases it returns false.
212    pub fn some_substr(&self, other: &Value) -> bool{
213        match (self, other) {
214            (Value::Str(s), Value::Str(o)) => s.iter().zip(o.iter()).any(|(a,b)| b.contains(a)),
215            _ => false,
216        }
217    }
218    /// Converts an internal value into a statically allocated slice of string slices. 
219    /// 
220    /// 
221    /// Transforms the underlying data by attempting to convert the current value into the desired string slice representation and immediately unwrapping the result. 
222    /// This operation guarantees that the conversion succeeds, provided the value is compatible with the expected type.
223    pub fn to_str(self) -> &'static [&'static str] {
224        self.try_into().unwrap()
225    }
226    pub fn to_int(self) -> &'static [i64] {
227        self.try_into().unwrap()
228    }
229    /// Converts a value into a static list of string slices by performing a conversion using the TryInto trait. 
230    /// Panics if the conversion fails, returning the resulting list of string slices upon success.
231    pub fn to_liststr(self) -> &'static [&'static [&'static str]] {
232        self.try_into().unwrap()
233    }
234    /// Converts the value into a static slice of booleans. 
235    /// This function attempts to transform the value into the corresponding boolean slice using an internal conversion mechanism and returns the result, panicking if the conversion fails.
236    pub fn to_bool(self) -> &'static [bool] {
237        self.try_into().unwrap()
238    }
239    /// Converts the boolean representation contained in the receiver into a bit vector. 
240    /// 
241    /// 
242    /// Transforms the boolean values of the instance into a sequential bit representation suitable for bitwise manipulation. 
243    /// This function extracts a boolean slice from the instance and converts it into an aggregated Bits value.
244    pub fn to_bits(self) -> Bits {
245        Bits::from_bit_siter(self.to_bool().iter().cloned())
246    }
247    /// Checks whether all boolean values in the object are true. 
248    /// 
249    /// 
250    /// Determines if the object, when representing boolean values, contains only true elements by iterating over the booleans and verifying each one is true; if the object does not represent booleans, it returns false.
251    pub fn is_all_true(&self) -> bool {
252        if let Self::Bool(b) = self {
253            b.iter().all(|x| *x)
254        } else { false }
255    }
256    /// Determines whether all boolean values in the instance evaluate to false.
257    /// 
258    /// Evaluates the content of the instance by checking if it represents a boolean collection, returning true only when every boolean in that collection is false; otherwise, it returns false, defaulting to false if the instance does not correspond to a boolean value.
259    pub fn is_all_false(&self) -> bool {
260        if let Self::Bool(b) = self {
261            b.iter().all(|x| !(*x))
262        } else { false }
263    }
264    /// Checks whether every string element within the value is empty.
265    /// 
266    /// Determines if the current instance contains a collection of strings and, if so, verifies that all strings have zero length, returning false if the value is of a different type.
267    pub fn is_all_empty(&self) -> bool {
268        if let Self::Str(b) = self {
269            b.iter().all(|x| x.is_empty())
270        } else { false }
271    }
272    /// Negates each boolean element in the input value and returns a new value with the negated booleans.
273    /// 
274    /// This function converts the current instance into a boolean slice, applies logical negation to every element, and then collects the results back into a new instance that encapsulates the transformed boolean values.
275    pub fn bool_not(self) -> Value {
276        let this = self.to_bool();
277        this.iter().map(|x| !x).galloc_scollect().into()
278    }
279    /// Computes the number of pairwise equal elements shared between two values. 
280    /// 
281    /// This function compares the corresponding elements of two instances by iterating over their internal collections and counting pairs that are equal. 
282    /// It supports several variants (such as integers, strings, floats, booleans, and lists) by aligning elements in parallel; when the compared types do not match, it returns zero.
283    /// 
284    pub fn eq_count(&self, other: &Self) -> usize {
285        match (self, other) {
286            (Self::Int(a1), Self::Int(a2)) => a1.iter().zip(a2.iter()).filter(|(a, b)| a == b).count(),
287            (Self::Str(a1), Self::Str(a2)) => a1.iter().zip(a2.iter()).filter(|(a, b)| a == b).count(),
288            (Self::Float(a1), Self::Float(a2)) => a1.iter().zip(a2.iter()).filter(|(a, b)| a == b).count(),
289            (Self::Bool(a1), Self::Bool(a2)) => a1.iter().zip(a2.iter()).filter(|(a, b)| a == b).count(),
290            (Self::ListInt(a1), Self::ListInt(a2)) => a1.iter().zip(a2.iter()).filter(|(a, b)| a == b).count(),
291            (Self::ListStr(a1), Self::ListStr(a2)) => a1.iter().zip(a2.iter()).filter(|(a, b)| a == b).count(),
292            _ => 0,
293        }
294    }
295    /// Compares two values and computes a bit mask representing elementwise equality. 
296    /// 
297    /// This function performs an elementwise comparison between the contents of two values of the same specific variant and returns an optional bit mask where each bit indicates whether corresponding elements are equal. 
298    /// If the two values do not belong to a matching variant that supports elementwise comparison (e.g., comparing an integer sequence to a boolean sequence), the function returns None.
299    /// 
300    pub fn eq_bits(&self, other: &Self) -> Option<Bits> {
301        match (self, other) {
302            (Self::Int(a1), Self::Int(a2)) => Some(Bits::from_bit_siter(a1.iter().zip(a2.iter()).map(|(a, b)| a == b))),
303            (Self::Str(a1), Self::Str(a2)) => Some(Bits::from_bit_siter(a1.iter().zip(a2.iter()).map(|(a, b)| a == b))),
304            (Self::Float(a1), Self::Float(a2)) => Some(Bits::from_bit_siter(a1.iter().zip(a2.iter()).map(|(a, b)| a == b))),
305            (Self::Bool(a1), Self::Bool(a2)) => Some(Bits::from_bit_siter(a1.iter().zip(a2.iter()).map(|(a, b)| a == b))),
306            (Self::ListInt(a1), Self::ListInt(a2)) => Some(Bits::from_bit_siter(a1.iter().zip(a2.iter()).map(|(a, b)| a == b))),
307            (Self::ListStr(a1), Self::ListStr(a2)) => Some(Bits::from_bit_siter(a1.iter().zip(a2.iter()).map(|(a, b)| a == b))),
308            _ => None,
309        }
310    }
311}
312
313
314#[derive(DebugCustom, Display, PartialEq, Eq, Hash, Clone, Copy, From)]
315/// Represents a constant value that abstracts various literal types and expressions. 
316/// This type encapsulates null, boolean, integer, string, floating-point, and expression values, each with respective formatting behavior for debugging and display purposes.
317pub enum ConstValue {
318    #[debug(fmt = "null")]
319    #[display(fmt = "null")]
320    Null,
321    #[debug(fmt = "{:?}", _0)]
322    #[display(fmt = "{:?}", _0)]
323    Bool(bool),
324    #[debug(fmt = "{:?}", _0)]
325    #[display(fmt = "{:?}", _0)]
326    Int(i64),
327    #[debug(fmt = "{:?}", _0)]
328    #[display(fmt = "{:?}", _0)]
329    Str(&'static str),
330    #[debug(fmt = "{:?}", _0)]
331    #[display(fmt = "{:?}", _0)]
332    Float(F64),
333    #[debug(fmt = "{:?}", _0)]
334    #[display(fmt = "{:?}", _0)]
335    BitVector(usize, u64),
336    #[debug(fmt = "{:?}", _0)]
337    #[display(fmt = "{:?}", _0)]
338    Expr(&'static Expr)
339}
340
341impl From<usize> for ConstValue {
342    /// Converts a usize value into a constant integer by casting it into a 64-bit integer. 
343    /// 
344    /// 
345    /// Transforms a usize into its integer representation wrapped as a constant, enabling its use in contexts where a constant of integer type is expected.
346    fn from(value: usize) -> Self {
347        Self::Int(value as i64)
348    }
349}
350impl From<u32> for ConstValue {
351    /// Converts an unsigned 32-bit integer into a constant integer value.
352    /// 
353    /// This function takes a u32 as input and returns the corresponding constant integer value by converting the input to a 64-bit integer internally.
354    fn from(value: u32) -> Self {
355        Self::Int(value as i64)
356    }
357}
358
359impl ConstValue {
360    /// Returns the type corresponding to a constant value instance. 
361    /// This function maps each variant of the constant value to its respective type: integer, boolean, string, float, or null, with the expression variant treated as null.
362    pub fn ty(&self) -> Type {
363        match self {
364            Self::Int(_) => Type::Int,
365            Self::Bool(_) => Type::Bool,
366            Self::Str(_) => Type::Str,
367            Self::Float(_) => Type::Float,
368            Self::BitVector(i, _) => Type::BitVector(*i),
369            Self::Null => Type::Null,
370            Self::Expr(_) => Type::Null,
371        }
372    }
373    #[inline(always)]
374    /// Returns an optional boolean extracted from the constant. 
375    /// This function checks if the constant holds a boolean value, and if so, returns it wrapped in Some; otherwise, it returns None.
376    pub fn as_bool(&self) -> Option<bool> { if let Self::Bool(b) = self { Some(*b) } else { None }}
377    #[inline(always)]
378    /// Returns the contained integer value if the input represents one. 
379    /// This function checks if the instance holds an integer and, if so, returns it inside an Option; otherwise, it yields None.
380    pub fn as_i64(&self) -> Option<i64> { if let Self::Int(b) = self { Some(*b) } else { None }}
381    /// Returns the contained integer value if the input represents one. 
382    /// This function checks if the instance holds an integer and, if so, returns it inside an Option; otherwise, it yields None.
383    pub fn as_bv(&self) -> Option<u64> { if let Self::BitVector(_, b) = self { Some(*b) } else { None }}
384    /// Converts the integer variant of a constant value into a usize if applicable. 
385    /// 
386    /// 
387    /// Returns an Option containing the usize representation when the constant is an integer; otherwise, it produces None.
388    pub fn as_usize(&self) -> Option<usize> { if let Self::Int(b) = self { Some(*b as usize) } else { None }}
389    /// Returns an Option wrapping a static string reference if the constant holds a string value, or None otherwise. 
390    /// This method inspects the constant value and, when it represents a string, provides access to the underlying static string slice, enabling string-specific processing while gracefully handling non-string cases.
391    pub fn as_str(&self) -> Option<&'static str> { if let Self::Str(b) = self { Some(*b) } else { None }}
392    /// Returns the floating-point value contained in the constant, if available.
393    /// 
394    /// Checks whether the constant is of the float variant, and if so, returns its underlying value wrapped in an Option; otherwise, it produces None.
395    pub fn as_float(&self) -> Option<F64> { if let Self::Float(b) = self { Some(*b) } else { None }}
396    /// Returns an optional static reference to an expression if the constant value represents an expression variant. 
397    /// This function checks whether the constant holds an expression and, if so, returns it wrapped in an option; otherwise, it returns None.
398    pub fn as_expr(&self) -> Option<&'static Expr> { if let Self::Expr(b) = self { Some(*b) } else { None }}
399    /// Extracts a floating-point value from a constant if it represents a float. 
400    /// This function checks the internal variant and, when it holds a float, retrieves the underlying f64 value wrapped in an Option; otherwise, it returns None.
401    pub fn as_f64(&self) -> Option<f64> { if let Self::Float(b) = self { Some(**b) } else { None }}
402    /// Checks whether a constant value is null. 
403    /// 
404    /// 
405    /// Determines if the constant value represents a null literal and returns true if so, otherwise false.
406    pub fn is_null(&self) -> bool { matches!(self, Self::Null) }
407    /// Converts a constant configuration into a collection value by replicating its underlying data over a specified length. 
408    /// 
409    /// 
410    /// Matches on the constant variant to determine the appropriate data type and constructs a value by repeatedly inserting the constant element for each index in the specified range. 
411    /// For boolean, integer, string, and float constants, a collection of repeated elements is created, while attempting to convert a null or expression constant results in a panic.
412    pub fn value(&self, len: usize) -> Value {
413        match self {
414            ConstValue::Bool(t) => Value::Bool((0..len).map(|_| *t).galloc_scollect()),
415            ConstValue::Int(t) => Value::Int((0..len).map(|_| *t).galloc_scollect()),
416            ConstValue::Str(t) => Value::Str((0..len).map(|_| *t).galloc_scollect()),
417            ConstValue::Float(f) => Value::Float((0..len).map(|_| *f).galloc_scollect()),
418            ConstValue::BitVector(i, a) => Value::BitVector(*i, (0..len).map(|_| *a).galloc_scollect()),
419            ConstValue::Null => panic!("Unable to convert Null to Value"),
420            ConstValue::Expr(_) => panic!("Unable to convert Expr to Value"),
421        }
422    }
423
424}
425
426/// Converts a vector of constant values into a value variant by deducing the target type from the first element. 
427/// 
428/// It traverses the provided vector, extracts the underlying primitive (boolean, integer, string, or float) for each constant, and collects the results into an allocated collection to form the corresponding value variant. 
429/// For constant expressions and null values, the conversion remains unimplemented.
430/// 
431pub fn consts_to_value(consts: Vec<ConstValue>) -> Value {
432    match consts[0] {
433        ConstValue::Null => todo!(),
434        ConstValue::Bool(_) => Value::Bool(consts.into_iter().map(|a| a.as_bool().unwrap()).galloc_scollect()),
435        ConstValue::Int(_) => Value::Int(consts.into_iter().map(|a| a.as_i64().unwrap()).galloc_scollect()),
436        ConstValue::Str(_) => Value::Str(consts.into_iter().map(|a| a.as_str().unwrap()).galloc_scollect()),
437        ConstValue::Float(_) => Value::Float(consts.into_iter().map(|a| a.as_float().unwrap()).galloc_scollect()),
438        ConstValue::BitVector(i, _) => Value::BitVector(i, consts.into_iter().map(|a| a.as_bv().unwrap()).galloc_scollect()),
439        ConstValue::Expr(_) => todo!(),
440    }
441}
442
443#[macro_export]
444/// This macro converts literal values into constant representations based on their type. 
445/// 
446/// It accepts the literal tokens true and false to produce boolean constants, and for a general literal it attempts runtime type checking to determine whether it is a string, an integer, or a floating-point value, converting it accordingly. 
447/// If the literal does not match any of these supported types, the macro causes a panic with an invalid literal message.
448/// 
449macro_rules! const_value {
450    (true) => {$crate::value::ConstValue::Bool(true)};
451    (false) => {$crate::value::ConstValue::Bool(false)};
452    ($l:literal) => { 
453        if let Some(f) = (&$l as &dyn std::any::Any).downcast_ref::<&str>() {
454            $crate::value::ConstValue::Str(f)
455        } else if let Some(f) = (&$l as &dyn std::any::Any).downcast_ref::<i32>() {
456            crate::value::ConstValue::Int(*f as i64)
457        } else if let Some(f) = (&$l as &dyn std::any::Any).downcast_ref::<f64>() {
458            crate::value::ConstValue::Float((*f as f64).into())
459        } else { panic!("Invalid literal {}", $l) }
460    };
461}