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}