Skip to main content

abi_types/
expr.rs

1use serde::de::{Deserializer, Error as DeError};
2use serde_derive::{Deserialize, Serialize};
3
4pub trait ConstantExpression {
5    fn is_constant(&self) -> bool;
6}
7
8impl ConstantExpression for ExprKind {
9    fn is_constant(&self) -> bool {
10        match self {
11            ExprKind::Literal(_) => true,
12            ExprKind::FieldRef(_) => false, // Field references are never constant
13            ExprKind::Sizeof(_) => true,    // Sizeof is constant once types are resolved
14            ExprKind::Alignof(_) => true,   // Alignof is constant once types are resolved
15
16            // Binary operations are constant if both operands are constant
17            ExprKind::Add(expr) => expr.left.is_constant() && expr.right.is_constant(),
18            ExprKind::Sub(expr) => expr.left.is_constant() && expr.right.is_constant(),
19            ExprKind::Mul(expr) => expr.left.is_constant() && expr.right.is_constant(),
20            ExprKind::Div(expr) => expr.left.is_constant() && expr.right.is_constant(),
21            ExprKind::Mod(expr) => expr.left.is_constant() && expr.right.is_constant(),
22            ExprKind::Pow(expr) => expr.left.is_constant() && expr.right.is_constant(),
23
24            ExprKind::BitAnd(expr) => expr.left.is_constant() && expr.right.is_constant(),
25            ExprKind::BitOr(expr) => expr.left.is_constant() && expr.right.is_constant(),
26            ExprKind::BitXor(expr) => expr.left.is_constant() && expr.right.is_constant(),
27            ExprKind::LeftShift(expr) => expr.left.is_constant() && expr.right.is_constant(),
28            ExprKind::RightShift(expr) => expr.left.is_constant() && expr.right.is_constant(),
29
30            ExprKind::Eq(expr) => expr.left.is_constant() && expr.right.is_constant(),
31            ExprKind::Ne(expr) => expr.left.is_constant() && expr.right.is_constant(),
32            ExprKind::Lt(expr) => expr.left.is_constant() && expr.right.is_constant(),
33            ExprKind::Gt(expr) => expr.left.is_constant() && expr.right.is_constant(),
34            ExprKind::Le(expr) => expr.left.is_constant() && expr.right.is_constant(),
35            ExprKind::Ge(expr) => expr.left.is_constant() && expr.right.is_constant(),
36
37            ExprKind::And(expr) => expr.left.is_constant() && expr.right.is_constant(),
38            ExprKind::Or(expr) => expr.left.is_constant() && expr.right.is_constant(),
39            ExprKind::Xor(expr) => expr.left.is_constant() && expr.right.is_constant(),
40
41            // Unary operations are constant if the operand is constant
42            ExprKind::BitNot(expr) => expr.operand.is_constant(),
43            ExprKind::Neg(expr) => expr.operand.is_constant(),
44            ExprKind::Not(expr) => expr.operand.is_constant(),
45            ExprKind::Popcount(expr) => expr.operand.is_constant(),
46        }
47    }
48}
49
50#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
51#[serde(rename_all = "kebab-case")]
52pub enum ExprKind {
53    Literal(LiteralExpr),
54    FieldRef(FieldRefExpr),
55    Sizeof(SizeofExpr),
56    Alignof(AlignofExpr),
57
58    // Binary ops
59    Add(AddExpr),
60    Sub(SubExpr),
61    Mul(MulExpr),
62    Div(DivExpr),
63    Mod(ModExpr),
64    Pow(PowExpr),
65
66    // Bitwise ops
67    BitAnd(BitAndExpr),
68    BitOr(BitOrExpr),
69    BitXor(BitXorExpr),
70    LeftShift(LeftShiftExpr),
71    RightShift(RightShiftExpr),
72
73    // Unary ops
74    BitNot(BitNotExpr),
75    Neg(NegExpr),
76    Not(NotExpr),
77    Popcount(PopcountExpr),
78
79    // Comparison ops
80    Eq(EqExpr),
81    Ne(NeExpr),
82    Lt(LtExpr),
83    Gt(GtExpr),
84    Le(LeExpr),
85    Ge(GeExpr),
86
87    // Logical ops
88    And(AndExpr),
89    Or(OrExpr),
90    Xor(XorExpr),
91}
92
93#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
94#[serde(rename_all = "kebab-case")]
95pub enum LiteralExpr {
96    U64(u64),
97    U32(u32),
98    U16(u16),
99    U8(u8),
100    I64(i64),
101    I32(i32),
102    I16(i16),
103    I8(i8),
104}
105
106#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
107#[serde(rename_all = "kebab-case")]
108pub struct FieldRefExpr {
109    #[serde(deserialize_with = "deserialize_field_path")]
110    pub path: Vec<String>, // Path to the field, e.g., ["parent", "field"] for parent.field
111}
112
113impl FieldRefExpr {
114    /// Convert the field reference path to C field access syntax
115    /// Handles paths like ["../tag"] or ["../hdr/type_slot"] and converts them to "tag" or "hdr.type_slot"
116    pub fn to_c_field_access(&self) -> String {
117        let tag_path: Vec<String> = self
118            .path
119            .iter()
120            .flat_map(|s| s.split('/'))
121            .map(|s| s.trim_start_matches(".."))
122            .filter(|s| !s.is_empty())
123            .fold(Vec::new(), |mut acc, seg| {
124                if seg.chars().all(|c| c.is_ascii_digit()) {
125                    let formatted = format!("[{}]", seg);
126                    if let Some(last) = acc.last_mut() {
127                        last.push_str(&formatted);
128                    } else {
129                        acc.push(formatted);
130                    }
131                } else {
132                    acc.push(seg.to_string());
133                }
134                acc
135            });
136
137        if tag_path.is_empty() {
138            "tag".to_string()
139        } else {
140            tag_path.join(".")
141        }
142    }
143}
144
145#[derive(Deserialize)]
146#[serde(untagged)]
147enum FieldPathSegmentValue {
148    Str(String),
149    Unsigned(u64),
150    Signed(i64),
151}
152
153fn deserialize_field_path<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
154where
155    D: Deserializer<'de>,
156{
157    let segments: Vec<FieldPathSegmentValue> = serde::Deserialize::deserialize(deserializer)?;
158    let mut out = Vec::with_capacity(segments.len());
159    for seg in segments {
160        let value = match seg {
161            FieldPathSegmentValue::Str(s) => s,
162            FieldPathSegmentValue::Unsigned(u) => u.to_string(),
163            FieldPathSegmentValue::Signed(i) => i.to_string(),
164        };
165        if value.is_empty() {
166            return Err(DeError::custom("field-ref path segments cannot be empty"));
167        }
168        out.push(value);
169    }
170    Ok(out)
171}
172
173#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
174#[serde(rename_all = "kebab-case")]
175pub struct SizeofExpr {
176    pub type_name: String, // Name of the type to get size of
177}
178
179#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
180#[serde(rename_all = "kebab-case")]
181pub struct AlignofExpr {
182    pub type_name: String, // Name of the type to get alignment of
183}
184
185// Binary arithmetic operations
186#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
187#[serde(rename_all = "kebab-case")]
188pub struct AddExpr {
189    pub left: Box<ExprKind>,
190    pub right: Box<ExprKind>,
191}
192
193#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
194#[serde(rename_all = "kebab-case")]
195pub struct SubExpr {
196    pub left: Box<ExprKind>,
197    pub right: Box<ExprKind>,
198}
199
200#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
201#[serde(rename_all = "kebab-case")]
202pub struct MulExpr {
203    pub left: Box<ExprKind>,
204    pub right: Box<ExprKind>,
205}
206
207#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
208#[serde(rename_all = "kebab-case")]
209pub struct DivExpr {
210    pub left: Box<ExprKind>,
211    pub right: Box<ExprKind>,
212}
213
214#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
215#[serde(rename_all = "kebab-case")]
216pub struct ModExpr {
217    pub left: Box<ExprKind>,
218    pub right: Box<ExprKind>,
219}
220
221#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
222#[serde(rename_all = "kebab-case")]
223pub struct PowExpr {
224    pub left: Box<ExprKind>,
225    pub right: Box<ExprKind>,
226}
227
228// Bitwise operations
229#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
230#[serde(rename_all = "kebab-case")]
231pub struct BitAndExpr {
232    pub left: Box<ExprKind>,
233    pub right: Box<ExprKind>,
234}
235
236#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
237#[serde(rename_all = "kebab-case")]
238pub struct BitOrExpr {
239    pub left: Box<ExprKind>,
240    pub right: Box<ExprKind>,
241}
242
243#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
244#[serde(rename_all = "kebab-case")]
245pub struct BitXorExpr {
246    pub left: Box<ExprKind>,
247    pub right: Box<ExprKind>,
248}
249
250#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
251#[serde(rename_all = "kebab-case")]
252pub struct LeftShiftExpr {
253    pub left: Box<ExprKind>,
254    pub right: Box<ExprKind>,
255}
256
257#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
258#[serde(rename_all = "kebab-case")]
259pub struct RightShiftExpr {
260    pub left: Box<ExprKind>,
261    pub right: Box<ExprKind>,
262}
263
264// Comparison operations
265#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
266#[serde(rename_all = "kebab-case")]
267pub struct EqExpr {
268    pub left: Box<ExprKind>,
269    pub right: Box<ExprKind>,
270}
271
272#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
273#[serde(rename_all = "kebab-case")]
274pub struct NeExpr {
275    pub left: Box<ExprKind>,
276    pub right: Box<ExprKind>,
277}
278
279#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
280#[serde(rename_all = "kebab-case")]
281pub struct LtExpr {
282    pub left: Box<ExprKind>,
283    pub right: Box<ExprKind>,
284}
285
286#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
287#[serde(rename_all = "kebab-case")]
288pub struct GtExpr {
289    pub left: Box<ExprKind>,
290    pub right: Box<ExprKind>,
291}
292
293#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
294#[serde(rename_all = "kebab-case")]
295pub struct LeExpr {
296    pub left: Box<ExprKind>,
297    pub right: Box<ExprKind>,
298}
299
300#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
301#[serde(rename_all = "kebab-case")]
302pub struct GeExpr {
303    pub left: Box<ExprKind>,
304    pub right: Box<ExprKind>,
305}
306
307// Logical operations
308#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
309#[serde(rename_all = "kebab-case")]
310pub struct AndExpr {
311    pub left: Box<ExprKind>,
312    pub right: Box<ExprKind>,
313}
314
315#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
316#[serde(rename_all = "kebab-case")]
317pub struct OrExpr {
318    pub left: Box<ExprKind>,
319    pub right: Box<ExprKind>,
320}
321
322#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
323#[serde(rename_all = "kebab-case")]
324pub struct XorExpr {
325    pub left: Box<ExprKind>,
326    pub right: Box<ExprKind>,
327}
328
329// Unary operations
330#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
331#[serde(rename_all = "kebab-case")]
332pub struct BitNotExpr {
333    pub operand: Box<ExprKind>,
334}
335
336#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
337#[serde(rename_all = "kebab-case")]
338pub struct NegExpr {
339    pub operand: Box<ExprKind>,
340}
341
342#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
343#[serde(rename_all = "kebab-case")]
344pub struct NotExpr {
345    pub operand: Box<ExprKind>,
346}
347
348#[derive(Serialize, Deserialize, Debug, PartialEq, Eq, Clone)]
349#[serde(rename_all = "kebab-case")]
350pub struct PopcountExpr {
351    pub operand: Box<ExprKind>,
352}
353
354// Expression evaluation functionality
355impl ExprKind {
356    /// Recursively evaluate this expression to a constant value if possible
357    /// Returns None if the expression contains unresolvable references (like field refs)
358    pub fn try_evaluate_constant(&self) -> Option<u64> {
359        match self {
360            ExprKind::Literal(literal) => {
361                match literal {
362                    LiteralExpr::U64(val) => Some(*val),
363                    LiteralExpr::U32(val) => Some(*val as u64),
364                    LiteralExpr::U16(val) => Some(*val as u64),
365                    LiteralExpr::U8(val) => Some(*val as u64),
366                    LiteralExpr::I64(val) if *val >= 0 => Some(*val as u64),
367                    LiteralExpr::I32(val) if *val >= 0 => Some(*val as u64),
368                    LiteralExpr::I16(val) if *val >= 0 => Some(*val as u64),
369                    LiteralExpr::I8(val) if *val >= 0 => Some(*val as u64),
370                    _ => None, // Negative values can't be converted to u64
371                }
372            }
373
374            ExprKind::Add(add_expr) => {
375                let left = add_expr.left.try_evaluate_constant()?;
376                let right = add_expr.right.try_evaluate_constant()?;
377                left.checked_add(right)
378            }
379
380            ExprKind::Sub(sub_expr) => {
381                let left = sub_expr.left.try_evaluate_constant()?;
382                let right = sub_expr.right.try_evaluate_constant()?;
383                left.checked_sub(right)
384            }
385
386            ExprKind::Mul(mul_expr) => {
387                let left = mul_expr.left.try_evaluate_constant()?;
388                let right = mul_expr.right.try_evaluate_constant()?;
389                left.checked_mul(right)
390            }
391
392            ExprKind::Div(div_expr) => {
393                let left = div_expr.left.try_evaluate_constant()?;
394                let right = div_expr.right.try_evaluate_constant()?;
395                if right == 0 {
396                    None // Division by zero
397                } else {
398                    left.checked_div(right)
399                }
400            }
401
402            ExprKind::Mod(mod_expr) => {
403                let left = mod_expr.left.try_evaluate_constant()?;
404                let right = mod_expr.right.try_evaluate_constant()?;
405                if right == 0 {
406                    None // Modulo by zero
407                } else {
408                    Some(left % right)
409                }
410            }
411
412            ExprKind::Pow(pow_expr) => {
413                let left = pow_expr.left.try_evaluate_constant()?;
414                let right = pow_expr.right.try_evaluate_constant()?;
415                if right <= u32::MAX as u64 {
416                    left.checked_pow(right as u32)
417                } else {
418                    None // Exponent too large
419                }
420            }
421
422            ExprKind::BitAnd(expr) => {
423                let left = expr.left.try_evaluate_constant()?;
424                let right = expr.right.try_evaluate_constant()?;
425                Some(left & right)
426            }
427
428            ExprKind::BitOr(expr) => {
429                let left = expr.left.try_evaluate_constant()?;
430                let right = expr.right.try_evaluate_constant()?;
431                Some(left | right)
432            }
433
434            ExprKind::BitXor(expr) => {
435                let left = expr.left.try_evaluate_constant()?;
436                let right = expr.right.try_evaluate_constant()?;
437                Some(left ^ right)
438            }
439
440            ExprKind::LeftShift(expr) => {
441                let left = expr.left.try_evaluate_constant()?;
442                let right = expr.right.try_evaluate_constant()?;
443                if right < 64 {
444                    left.checked_shl(right as u32)
445                } else {
446                    None // Shift amount too large
447                }
448            }
449
450            ExprKind::RightShift(expr) => {
451                let left = expr.left.try_evaluate_constant()?;
452                let right = expr.right.try_evaluate_constant()?;
453                if right < 64 {
454                    Some(left >> right)
455                } else {
456                    None // Shift amount too large
457                }
458            }
459
460            ExprKind::BitNot(expr) => {
461                let operand = expr.operand.try_evaluate_constant()?;
462                Some(!operand)
463            }
464
465            ExprKind::Neg(expr) => {
466                // Negation only works if the result can fit in i64 and be converted back to u64
467                let operand = expr.operand.try_evaluate_constant()?;
468                if operand <= i64::MAX as u64 {
469                    let signed = operand as i64;
470                    let negated = -signed;
471                    if negated >= 0 {
472                        Some(negated as u64)
473                    } else {
474                        None // Negative result
475                    }
476                } else {
477                    None // Can't negate large unsigned values
478                }
479            }
480
481            ExprKind::Popcount(expr) => {
482                let operand = expr.operand.try_evaluate_constant()?;
483                Some(operand.count_ones() as u64)
484            }
485
486            // These operations can't be evaluated to constants without more context
487            ExprKind::FieldRef(_) => None, // Would need field value resolution
488            ExprKind::Sizeof(_) => None,   // Would need type size information
489            ExprKind::Alignof(_) => None,  // Would need type alignment information
490            ExprKind::Eq(_)
491            | ExprKind::Ne(_)
492            | ExprKind::Lt(_)
493            | ExprKind::Gt(_)
494            | ExprKind::Le(_)
495            | ExprKind::Ge(_) => None, // Comparison results are booleans
496            ExprKind::And(_) | ExprKind::Or(_) | ExprKind::Xor(_) | ExprKind::Not(_) => None, // Logical ops
497        }
498    }
499
500    /// Format the expression as a C-style mathematical expression
501    /// This format is compatible with C, Rust, and TypeScript for most operations
502    pub fn to_c_string(&self) -> String {
503        match self {
504            ExprKind::Literal(lit) => match lit {
505                LiteralExpr::U64(val) => val.to_string(),
506                LiteralExpr::U32(val) => val.to_string(),
507                LiteralExpr::U16(val) => val.to_string(),
508                LiteralExpr::U8(val) => val.to_string(),
509                LiteralExpr::I64(val) => val.to_string(),
510                LiteralExpr::I32(val) => val.to_string(),
511                LiteralExpr::I16(val) => val.to_string(),
512                LiteralExpr::I8(val) => val.to_string(),
513            },
514            ExprKind::FieldRef(field_ref) => field_ref.path.join("."),
515            ExprKind::Sizeof(sizeof_expr) => format!("sizeof({})", sizeof_expr.type_name),
516            ExprKind::Alignof(alignof_expr) => format!("alignof({})", alignof_expr.type_name),
517
518            // Binary operations
519            ExprKind::Add(e) => format!("({}+{})", e.left.to_c_string(), e.right.to_c_string()),
520            ExprKind::Sub(e) => format!("({}-{})", e.left.to_c_string(), e.right.to_c_string()),
521            ExprKind::Mul(e) => format!("({}*{})", e.left.to_c_string(), e.right.to_c_string()),
522            ExprKind::Div(e) => format!("({}/{})", e.left.to_c_string(), e.right.to_c_string()),
523            ExprKind::Mod(e) => format!("({}%{})", e.left.to_c_string(), e.right.to_c_string()),
524            ExprKind::Pow(e) => format!("pow({},{})", e.left.to_c_string(), e.right.to_c_string()),
525
526            // Bitwise operations
527            ExprKind::BitAnd(e) => format!("({}&{})", e.left.to_c_string(), e.right.to_c_string()),
528            ExprKind::BitOr(e) => format!("({}|{})", e.left.to_c_string(), e.right.to_c_string()),
529            ExprKind::BitXor(e) => format!("({}^{})", e.left.to_c_string(), e.right.to_c_string()),
530            ExprKind::LeftShift(e) => {
531                format!("({}<<{})", e.left.to_c_string(), e.right.to_c_string())
532            }
533            ExprKind::RightShift(e) => {
534                format!("({}>>{})", e.left.to_c_string(), e.right.to_c_string())
535            }
536
537            // Unary operations
538            ExprKind::BitNot(e) => format!("~({})", e.operand.to_c_string()),
539            ExprKind::Neg(e) => format!("-({})", e.operand.to_c_string()),
540            ExprKind::Not(e) => format!("!({})", e.operand.to_c_string()),
541            ExprKind::Popcount(e) => format!("__builtin_popcount({})", e.operand.to_c_string()),
542
543            // Comparison operations
544            ExprKind::Eq(e) => format!("({}=={})", e.left.to_c_string(), e.right.to_c_string()),
545            ExprKind::Ne(e) => format!("({}!={})", e.left.to_c_string(), e.right.to_c_string()),
546            ExprKind::Lt(e) => format!("({}<{})", e.left.to_c_string(), e.right.to_c_string()),
547            ExprKind::Gt(e) => format!("({}>{})", e.left.to_c_string(), e.right.to_c_string()),
548            ExprKind::Le(e) => format!("({}<={})", e.left.to_c_string(), e.right.to_c_string()),
549            ExprKind::Ge(e) => format!("({}>={})", e.left.to_c_string(), e.right.to_c_string()),
550
551            // Logical operations
552            ExprKind::And(e) => format!("({}&&{})", e.left.to_c_string(), e.right.to_c_string()),
553            ExprKind::Or(e) => format!("({}||{})", e.left.to_c_string(), e.right.to_c_string()),
554            ExprKind::Xor(e) => format!("({}^^{})", e.left.to_c_string(), e.right.to_c_string()), // Note: ^^ is not standard in C
555        }
556    }
557
558    /// Generate a debug string representation of the expression
559    pub fn to_debug_string(&self) -> String {
560        match self {
561            ExprKind::Literal(literal) => match literal {
562                LiteralExpr::U64(val) => format!("Literal(U64: {})", val),
563                LiteralExpr::U32(val) => format!("Literal(U32: {})", val),
564                LiteralExpr::U16(val) => format!("Literal(U16: {})", val),
565                LiteralExpr::U8(val) => format!("Literal(U8: {})", val),
566                LiteralExpr::I64(val) => format!("Literal(I64: {})", val),
567                LiteralExpr::I32(val) => format!("Literal(I32: {})", val),
568                LiteralExpr::I16(val) => format!("Literal(I16: {})", val),
569                LiteralExpr::I8(val) => format!("Literal(I8: {})", val),
570            },
571            ExprKind::FieldRef(field_ref) => {
572                format!("FieldRef(path: [{}])", field_ref.path.join(", "))
573            }
574            ExprKind::Sizeof(sizeof_expr) => {
575                format!("Sizeof(type: {})", sizeof_expr.type_name)
576            }
577            ExprKind::Alignof(alignof_expr) => {
578                format!("Alignof(type: {})", alignof_expr.type_name)
579            }
580            ExprKind::Add(expr) => {
581                format!(
582                    "Add({} + {})",
583                    expr.left.to_debug_string(),
584                    expr.right.to_debug_string()
585                )
586            }
587            ExprKind::Sub(expr) => {
588                format!(
589                    "Sub({} - {})",
590                    expr.left.to_debug_string(),
591                    expr.right.to_debug_string()
592                )
593            }
594            ExprKind::Mul(expr) => {
595                format!(
596                    "Mul({} * {})",
597                    expr.left.to_debug_string(),
598                    expr.right.to_debug_string()
599                )
600            }
601            ExprKind::Div(expr) => {
602                format!(
603                    "Div({} / {})",
604                    expr.left.to_debug_string(),
605                    expr.right.to_debug_string()
606                )
607            }
608            ExprKind::Mod(expr) => {
609                format!(
610                    "Mod({} % {})",
611                    expr.left.to_debug_string(),
612                    expr.right.to_debug_string()
613                )
614            }
615            ExprKind::Pow(expr) => {
616                format!(
617                    "Pow({} ** {})",
618                    expr.left.to_debug_string(),
619                    expr.right.to_debug_string()
620                )
621            }
622            ExprKind::BitAnd(expr) => {
623                format!(
624                    "BitAnd({} & {})",
625                    expr.left.to_debug_string(),
626                    expr.right.to_debug_string()
627                )
628            }
629            ExprKind::BitOr(expr) => {
630                format!(
631                    "BitOr({} | {})",
632                    expr.left.to_debug_string(),
633                    expr.right.to_debug_string()
634                )
635            }
636            ExprKind::BitXor(expr) => {
637                format!(
638                    "BitXor({} ^ {})",
639                    expr.left.to_debug_string(),
640                    expr.right.to_debug_string()
641                )
642            }
643            ExprKind::LeftShift(expr) => {
644                format!(
645                    "LeftShift({} << {})",
646                    expr.left.to_debug_string(),
647                    expr.right.to_debug_string()
648                )
649            }
650            ExprKind::RightShift(expr) => {
651                format!(
652                    "RightShift({} >> {})",
653                    expr.left.to_debug_string(),
654                    expr.right.to_debug_string()
655                )
656            }
657            ExprKind::BitNot(expr) => {
658                format!("BitNot(~{})", expr.operand.to_debug_string())
659            }
660            ExprKind::Neg(expr) => {
661                format!("Neg(-{})", expr.operand.to_debug_string())
662            }
663            ExprKind::Not(expr) => {
664                format!("Not(!{})", expr.operand.to_debug_string())
665            }
666            ExprKind::Popcount(expr) => {
667                format!("Popcount(popcount({}))", expr.operand.to_debug_string())
668            }
669            ExprKind::Eq(expr) => {
670                format!(
671                    "Eq({} == {})",
672                    expr.left.to_debug_string(),
673                    expr.right.to_debug_string()
674                )
675            }
676            ExprKind::Ne(expr) => {
677                format!(
678                    "Ne({} != {})",
679                    expr.left.to_debug_string(),
680                    expr.right.to_debug_string()
681                )
682            }
683            ExprKind::Lt(expr) => {
684                format!(
685                    "Lt({} < {})",
686                    expr.left.to_debug_string(),
687                    expr.right.to_debug_string()
688                )
689            }
690            ExprKind::Gt(expr) => {
691                format!(
692                    "Gt({} > {})",
693                    expr.left.to_debug_string(),
694                    expr.right.to_debug_string()
695                )
696            }
697            ExprKind::Le(expr) => {
698                format!(
699                    "Le({} <= {})",
700                    expr.left.to_debug_string(),
701                    expr.right.to_debug_string()
702                )
703            }
704            ExprKind::Ge(expr) => {
705                format!(
706                    "Ge({} >= {})",
707                    expr.left.to_debug_string(),
708                    expr.right.to_debug_string()
709                )
710            }
711            ExprKind::And(expr) => {
712                format!(
713                    "And({} && {})",
714                    expr.left.to_debug_string(),
715                    expr.right.to_debug_string()
716                )
717            }
718            ExprKind::Or(expr) => {
719                format!(
720                    "Or({} || {})",
721                    expr.left.to_debug_string(),
722                    expr.right.to_debug_string()
723                )
724            }
725            ExprKind::Xor(expr) => {
726                format!(
727                    "Xor({} ^^ {})",
728                    expr.left.to_debug_string(),
729                    expr.right.to_debug_string()
730                )
731            }
732        }
733    }
734}