• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Qiskit / qiskit / 15690052436

16 Jun 2025 07:27PM CUT coverage: 88.003% (-0.006%) from 88.009%
15690052436

Pull #14361

github

web-flow
Merge 7fa9dd602 into 9ec464af4
Pull Request #14361: Oxidize `VariableMapper` and uses.

512 of 597 new or added lines in 4 files covered. (85.76%)

12 existing lines in 4 files now uncovered.

83520 of 94906 relevant lines covered (88.0%)

514545.5 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

88.36
/crates/circuit/src/classical/expr/expr.rs
1
// This code is part of Qiskit.
2
//
3
// (C) Copyright IBM 2025
4
//
5
// This code is licensed under the Apache License, Version 2.0. You may
6
// obtain a copy of this license in the LICENSE.txt file in the root directory
7
// of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
//
9
// Any modifications or derivative works of this code must retain this
10
// copyright notice, and modified files need to carry a notice indicating
11
// that they have been altered from the originals.
12

13
use crate::classical::expr::{Binary, Cast, Index, Stretch, Unary, Value, Var};
14
use crate::classical::types::Type;
15
use pyo3::prelude::*;
16
use pyo3::{intern, IntoPyObjectExt};
17

18
/// A classical expression.
19
///
20
/// Variants that themselves contain [Expr]s are boxed. This is done instead
21
/// of boxing the contained [Expr]s within the specific type to reduce the
22
/// number of boxes we need (e.g. Binary would otherwise contain two boxed
23
/// expressions).
24
#[derive(Clone, Debug, PartialEq)]
25
pub enum Expr {
26
    Unary(Box<Unary>),
27
    Binary(Box<Binary>),
28
    Cast(Box<Cast>),
29
    Value(Value),
30
    Var(Var),
31
    Stretch(Stretch),
32
    Index(Box<Index>),
33
}
34

35
#[derive(Debug, PartialEq)]
36
pub enum ExprRef<'a> {
37
    Unary(&'a Unary),
38
    Binary(&'a Binary),
39
    Cast(&'a Cast),
40
    Value(&'a Value),
41
    Var(&'a Var),
42
    Stretch(&'a Stretch),
43
    Index(&'a Index),
44
}
45

46
#[derive(Debug, PartialEq)]
47
pub enum ExprRefMut<'a> {
48
    Unary(&'a mut Unary),
49
    Binary(&'a mut Binary),
50
    Cast(&'a mut Cast),
51
    Value(&'a mut Value),
52
    Var(&'a mut Var),
53
    Stretch(&'a mut Stretch),
54
    Index(&'a mut Index),
55
}
56

57
#[derive(Clone, Debug, PartialEq)]
58
pub enum IdentifierRef<'a> {
59
    Var(&'a Var),
60
    Stretch(&'a Stretch),
61
}
62

63
impl Expr {
64
    /// Converts from `&Expr` to `ExprRef`.
65
    pub fn as_ref(&self) -> ExprRef<'_> {
9,736✔
66
        match self {
9,736✔
67
            Expr::Unary(u) => ExprRef::Unary(u.as_ref()),
178✔
68
            Expr::Binary(b) => ExprRef::Binary(b.as_ref()),
1,388✔
69
            Expr::Cast(c) => ExprRef::Cast(c.as_ref()),
82✔
70
            Expr::Value(v) => ExprRef::Value(v),
2,693✔
71
            Expr::Var(v) => ExprRef::Var(v),
5,375✔
72
            Expr::Stretch(s) => ExprRef::Stretch(s),
4✔
73
            Expr::Index(i) => ExprRef::Index(i.as_ref()),
16✔
74
        }
75
    }
9,736✔
76

77
    /// Converts from `&mut Expr` to `ExprRefMut`.
78
    pub fn as_mut(&mut self) -> ExprRefMut<'_> {
69✔
79
        match self {
69✔
80
            Expr::Unary(u) => ExprRefMut::Unary(u.as_mut()),
7✔
81
            Expr::Binary(b) => ExprRefMut::Binary(b.as_mut()),
16✔
82
            Expr::Cast(c) => ExprRefMut::Cast(c.as_mut()),
4✔
83
            Expr::Value(v) => ExprRefMut::Value(v),
10✔
84
            Expr::Var(v) => ExprRefMut::Var(v),
30✔
85
            Expr::Stretch(s) => ExprRefMut::Stretch(s),
2✔
NEW
86
            Expr::Index(i) => ExprRefMut::Index(i.as_mut()),
×
87
        }
88
    }
69✔
89

90
    /// The const-ness of the expression.
91
    pub fn is_const(&self) -> bool {
3,696✔
92
        match self {
3,696✔
93
            Expr::Unary(u) => u.constant,
38✔
94
            Expr::Binary(b) => b.constant,
374✔
95
            Expr::Cast(c) => c.constant,
104✔
96
            Expr::Value(_) => true,
1,412✔
97
            Expr::Var(_) => false,
1,684✔
98
            Expr::Stretch(_) => true,
78✔
99
            Expr::Index(i) => i.constant,
6✔
100
        }
101
    }
3,696✔
102

103
    /// The expression's [Type].
104
    pub fn ty(&self) -> Type {
×
105
        match self {
×
106
            Expr::Unary(u) => u.ty,
×
107
            Expr::Binary(b) => b.ty,
×
108
            Expr::Cast(c) => c.ty,
×
109
            Expr::Value(v) => match v {
×
110
                Value::Duration(_) => Type::Duration,
×
111
                Value::Float { ty, .. } => *ty,
×
112
                Value::Uint { ty, .. } => *ty,
×
113
            },
114
            Expr::Var(v) => match v {
×
115
                Var::Standalone { ty, .. } => *ty,
×
116
                Var::Bit { .. } => Type::Bool,
×
117
                Var::Register { ty, .. } => *ty,
×
118
            },
119
            Expr::Stretch(_) => Type::Duration,
×
120
            Expr::Index(i) => i.ty,
×
121
        }
122
    }
×
123

124
    /// Returns an iterator over the identifier nodes in this expression in some
125
    /// deterministic order.
126
    pub fn identifiers(&self) -> impl Iterator<Item = IdentifierRef<'_>> {
1✔
127
        IdentIterator(ExprIterator { stack: vec![self] })
1✔
128
    }
1✔
129

130
    /// Returns an iterator over the [Var] nodes in this expression in some
131
    /// deterministic order.
132
    pub fn vars(&self) -> impl Iterator<Item = &Var> {
6,666✔
133
        VarIterator(ExprIterator { stack: vec![self] })
6,666✔
134
    }
6,666✔
135

136
    /// Returns an iterator over all nodes in this expression in some deterministic
137
    /// order.
138
    pub fn iter(&self) -> impl Iterator<Item = ExprRef> {
1✔
139
        ExprIterator { stack: vec![self] }
1✔
140
    }
1✔
141

142
    /// Visits all nodes by mutable reference, in a post-order traversal.
143
    pub fn visit_mut<F>(&mut self, mut visitor: F) -> PyResult<()>
26✔
144
    where
26✔
145
        F: FnMut(ExprRefMut) -> PyResult<()>,
26✔
146
    {
26✔
147
        self.visit_mut_impl(&mut visitor)
26✔
148
    }
26✔
149

150
    fn visit_mut_impl<F>(&mut self, visitor: &mut F) -> PyResult<()>
69✔
151
    where
69✔
152
        F: FnMut(ExprRefMut) -> PyResult<()>,
69✔
153
    {
69✔
154
        match self {
69✔
155
            Expr::Unary(u) => u.operand.visit_mut_impl(visitor)?,
7✔
156
            Expr::Binary(b) => {
16✔
157
                b.left.visit_mut_impl(visitor)?;
16✔
158
                b.right.visit_mut_impl(visitor)?;
16✔
159
            }
160
            Expr::Cast(c) => c.operand.visit_mut_impl(visitor)?,
4✔
161
            Expr::Value(_) => {}
10✔
162
            Expr::Var(_) => {}
30✔
163
            Expr::Stretch(_) => {}
2✔
NEW
164
            Expr::Index(i) => {
×
NEW
165
                i.target.visit_mut_impl(visitor)?;
×
NEW
166
                i.index.visit_mut_impl(visitor)?;
×
167
            }
168
        }
169
        visitor(self.as_mut())
69✔
170
    }
69✔
171
}
172

173
/// A private iterator over the [Expr] nodes of an expression
174
/// by reference.
175
///
176
/// The first node reference returned is the [Expr] itself.
177
struct ExprIterator<'a> {
178
    stack: Vec<&'a Expr>,
179
}
180

181
impl<'a> Iterator for ExprIterator<'a> {
182
    type Item = ExprRef<'a>;
183

184
    fn next(&mut self) -> Option<Self::Item> {
16,403✔
185
        let expr = self.stack.pop()?;
16,403✔
186
        match expr {
9,736✔
187
            Expr::Unary(u) => {
178✔
188
                self.stack.push(&u.operand);
178✔
189
            }
178✔
190
            Expr::Binary(b) => {
1,388✔
191
                self.stack.push(&b.left);
1,388✔
192
                self.stack.push(&b.right);
1,388✔
193
            }
1,388✔
194
            Expr::Cast(c) => self.stack.push(&c.operand),
82✔
195
            Expr::Value(_) => {}
2,693✔
196
            Expr::Var(_) => {}
5,375✔
197
            Expr::Stretch(_) => {}
4✔
198
            Expr::Index(i) => {
16✔
199
                self.stack.push(&i.index);
16✔
200
                self.stack.push(&i.target);
16✔
201
            }
16✔
202
        }
203
        Some(expr.as_ref())
9,736✔
204
    }
16,403✔
205
}
206

207
/// A private iterator over the [Var] nodes contained within an [Expr].
208
struct VarIterator<'a>(ExprIterator<'a>);
209

210
impl<'a> Iterator for VarIterator<'a> {
211
    type Item = &'a Var;
212

213
    fn next(&mut self) -> Option<Self::Item> {
12,038✔
214
        for expr in self.0.by_ref() {
12,042✔
215
            if let ExprRef::Var(v) = expr {
9,722✔
216
                return Some(v);
5,373✔
217
            }
4,349✔
218
        }
219
        None
6,665✔
220
    }
12,038✔
221
}
222

223
/// A private iterator over the [Var] and [Stretch] nodes contained within an [Expr].
224
struct IdentIterator<'a>(ExprIterator<'a>);
225

226
impl<'a> Iterator for IdentIterator<'a> {
227
    type Item = IdentifierRef<'a>;
228

229
    fn next(&mut self) -> Option<Self::Item> {
4✔
230
        for expr in self.0.by_ref() {
7✔
231
            if let ExprRef::Var(v) = expr {
7✔
232
                return Some(IdentifierRef::Var(v));
1✔
233
            }
6✔
234
            if let ExprRef::Stretch(s) = expr {
6✔
235
                return Some(IdentifierRef::Stretch(s));
2✔
236
            }
4✔
237
        }
238
        None
1✔
239
    }
4✔
240
}
241

242
impl From<Unary> for Expr {
243
    fn from(value: Unary) -> Self {
2✔
244
        Expr::Unary(Box::new(value))
2✔
245
    }
2✔
246
}
247

248
impl From<Box<Unary>> for Expr {
249
    fn from(value: Box<Unary>) -> Self {
×
250
        Expr::Unary(value)
×
251
    }
×
252
}
253

254
impl From<Binary> for Expr {
255
    fn from(value: Binary) -> Self {
11✔
256
        Expr::Binary(Box::new(value))
11✔
257
    }
11✔
258
}
259

260
impl From<Box<Binary>> for Expr {
261
    fn from(value: Box<Binary>) -> Self {
×
262
        Expr::Binary(value)
×
263
    }
×
264
}
265

266
impl From<Cast> for Expr {
267
    fn from(value: Cast) -> Self {
×
268
        Expr::Cast(Box::new(value))
×
269
    }
×
270
}
271

272
impl From<Box<Cast>> for Expr {
273
    fn from(value: Box<Cast>) -> Self {
×
274
        Expr::Cast(value)
×
275
    }
×
276
}
277

278
impl From<Value> for Expr {
279
    fn from(value: Value) -> Self {
4✔
280
        Expr::Value(value)
4✔
281
    }
4✔
282
}
283

284
impl From<Var> for Expr {
285
    fn from(value: Var) -> Self {
6✔
286
        Expr::Var(value)
6✔
287
    }
6✔
288
}
289

290
impl From<Stretch> for Expr {
291
    fn from(value: Stretch) -> Self {
6✔
292
        Expr::Stretch(value)
6✔
293
    }
6✔
294
}
295

296
impl From<Index> for Expr {
297
    fn from(value: Index) -> Self {
×
298
        Expr::Index(Box::new(value))
×
299
    }
×
300
}
301

302
impl From<Box<Index>> for Expr {
303
    fn from(value: Box<Index>) -> Self {
×
304
        Expr::Index(value)
×
305
    }
×
306
}
307

308
/// Root base class of all nodes in the expression tree.  The base case should never be
309
/// instantiated directly.
310
///
311
/// This must not be subclassed by users; subclasses form the internal data of the representation of
312
/// expressions, and it does not make sense to add more outside of Qiskit library code.
313
///
314
/// All subclasses are responsible for setting their ``type`` attribute in their ``__init__``, and
315
/// should not call the parent initializer."""
316
#[pyclass(
12✔
317
    eq,
12✔
318
    hash,
12✔
319
    subclass,
12✔
320
    frozen,
12✔
321
    name = "Expr",
12✔
322
    module = "qiskit._accelerate.circuit.classical.expr"
12✔
323
)]
12✔
324
#[derive(PartialEq, Clone, Copy, Debug, Hash)]
325
pub struct PyExpr(pub ExprKind); // ExprKind is used for fast extraction from Python
326

327
#[pymethods]
×
328
impl PyExpr {
329
    /// Call the relevant ``visit_*`` method on the given :class:`ExprVisitor`.  The usual entry
330
    /// point for a simple visitor is to construct it, and then call :meth:`accept` on the root
331
    /// object to be visited.  For example::
332
    ///
333
    ///     expr = ...
334
    ///     visitor = MyVisitor()
335
    ///     visitor.accept(expr)
336
    ///
337
    /// Subclasses of :class:`Expr` should override this to call the correct virtual method on the
338
    /// visitor.  This implements double dispatch with the visitor."""
339
    /// return visitor.visit_generic(self)
340
    fn accept<'py>(
×
341
        slf: PyRef<'py, Self>,
×
342
        visitor: &Bound<'py, PyAny>,
×
343
    ) -> PyResult<Bound<'py, PyAny>> {
×
344
        visitor.call_method1(intern!(visitor.py(), "visit_generic"), (slf,))
×
345
    }
×
346
}
347

348
/// The expression's kind, used internally during Python instance extraction to avoid
349
/// `isinstance` checks.
350
#[repr(u8)]
351
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
352
pub enum ExprKind {
353
    Unary,
354
    Binary,
355
    Value,
356
    Var,
357
    Cast,
358
    Stretch,
359
    Index,
360
}
361

362
impl<'py> IntoPyObject<'py> for Expr {
363
    type Target = PyAny;
364
    type Output = Bound<'py, PyAny>;
365
    type Error = PyErr;
366

367
    fn into_pyobject(self, py: Python<'py>) -> Result<Self::Output, Self::Error> {
5,072✔
368
        match self {
5,072✔
369
            Expr::Unary(u) => u.into_bound_py_any(py),
148✔
370
            Expr::Binary(b) => b.into_bound_py_any(py),
1,046✔
371
            Expr::Cast(c) => c.into_bound_py_any(py),
48✔
372
            Expr::Value(v) => v.into_bound_py_any(py),
1,454✔
373
            Expr::Var(v) => v.into_bound_py_any(py),
2,246✔
374
            Expr::Stretch(s) => s.into_bound_py_any(py),
110✔
375
            Expr::Index(i) => i.into_bound_py_any(py),
20✔
376
        }
377
    }
5,072✔
378
}
379

380
impl<'py> FromPyObject<'py> for Expr {
381
    fn extract_bound(ob: &Bound<'py, PyAny>) -> PyResult<Self> {
29,194✔
382
        let expr: PyRef<'_, PyExpr> = ob.downcast()?.borrow();
29,194✔
383
        match expr.0 {
11,788✔
384
            ExprKind::Unary => Ok(Expr::Unary(Box::new(ob.extract()?))),
204✔
385
            ExprKind::Binary => Ok(Expr::Binary(Box::new(ob.extract()?))),
1,664✔
386
            ExprKind::Value => Ok(Expr::Value(ob.extract()?)),
3,952✔
387
            ExprKind::Var => Ok(Expr::Var(ob.extract()?)),
5,716✔
388
            ExprKind::Cast => Ok(Expr::Cast(Box::new(ob.extract()?))),
154✔
389
            ExprKind::Stretch => Ok(Expr::Stretch(ob.extract()?)),
78✔
390
            ExprKind::Index => Ok(Expr::Index(Box::new(ob.extract()?))),
20✔
391
        }
392
    }
29,194✔
393
}
394

395
#[cfg(test)]
396
mod tests {
397
    use crate::bit::ShareableClbit;
398
    use crate::classical::expr::{
399
        Binary, BinaryOp, Expr, ExprRef, ExprRefMut, IdentifierRef, Stretch, Unary, UnaryOp, Value,
400
        Var,
401
    };
402
    use crate::classical::types::Type;
403
    use crate::duration::Duration;
404
    use pyo3::PyResult;
405
    use uuid::Uuid;
406

407
    #[test]
408
    fn test_vars() {
1✔
409
        let expr: Expr = Binary {
1✔
410
            op: BinaryOp::BitAnd,
1✔
411
            left: Unary {
1✔
412
                op: UnaryOp::BitNot,
1✔
413
                operand: Var::Standalone {
1✔
414
                    uuid: Uuid::new_v4().as_u128(),
1✔
415
                    name: "test".to_string(),
1✔
416
                    ty: Type::Bool,
1✔
417
                }
1✔
418
                .into(),
1✔
419
                ty: Type::Bool,
1✔
420
                constant: false,
1✔
421
            }
1✔
422
            .into(),
1✔
423
            right: Var::Bit {
1✔
424
                bit: ShareableClbit::new_anonymous(),
1✔
425
            }
1✔
426
            .into(),
1✔
427
            ty: Type::Bool,
1✔
428
            constant: false,
1✔
429
        }
1✔
430
        .into();
1✔
431

1✔
432
        let vars: Vec<&Var> = expr.vars().collect();
1✔
433
        assert!(matches!(
1✔
434
            vars.as_slice(),
1✔
435
            [Var::Bit { .. }, Var::Standalone { .. }]
1✔
436
        ));
437
    }
1✔
438

439
    #[test]
440
    fn test_identifiers() {
1✔
441
        let expr: Expr = Binary {
1✔
442
            op: BinaryOp::Mul,
1✔
443
            left: Binary {
1✔
444
                op: BinaryOp::Add,
1✔
445
                left: Var::Standalone {
1✔
446
                    uuid: Uuid::new_v4().as_u128(),
1✔
447
                    name: "test".to_string(),
1✔
448
                    ty: Type::Duration,
1✔
449
                }
1✔
450
                .into(),
1✔
451
                right: Stretch {
1✔
452
                    uuid: Uuid::new_v4().as_u128(),
1✔
453
                    name: "test".to_string(),
1✔
454
                }
1✔
455
                .into(),
1✔
456
                ty: Type::Duration,
1✔
457
                constant: false,
1✔
458
            }
1✔
459
            .into(),
1✔
460
            right: Binary {
1✔
461
                op: BinaryOp::Div,
1✔
462
                left: Stretch {
1✔
463
                    uuid: Uuid::new_v4().as_u128(),
1✔
464
                    name: "test".to_string(),
1✔
465
                }
1✔
466
                .into(),
1✔
467
                right: Value::Duration(Duration::dt(1000)).into(),
1✔
468
                ty: Type::Float,
1✔
469
                constant: true,
1✔
470
            }
1✔
471
            .into(),
1✔
472
            ty: Type::Bool,
1✔
473
            constant: false,
1✔
474
        }
1✔
475
        .into();
1✔
476

1✔
477
        let identifiers: Vec<IdentifierRef> = expr.identifiers().collect();
1✔
478
        assert!(matches!(
1✔
479
            identifiers.as_slice(),
1✔
480
            [
1✔
481
                IdentifierRef::Stretch(Stretch { .. }),
1✔
482
                IdentifierRef::Stretch(Stretch { .. }),
1✔
483
                IdentifierRef::Var(Var::Standalone { .. }),
1✔
484
            ]
1✔
485
        ));
486
    }
1✔
487

488
    #[test]
489
    fn test_iter() {
1✔
490
        let expr: Expr = Binary {
1✔
491
            op: BinaryOp::Mul,
1✔
492
            left: Binary {
1✔
493
                op: BinaryOp::Add,
1✔
494
                left: Var::Standalone {
1✔
495
                    uuid: Uuid::new_v4().as_u128(),
1✔
496
                    name: "test".to_string(),
1✔
497
                    ty: Type::Duration,
1✔
498
                }
1✔
499
                .into(),
1✔
500
                right: Stretch {
1✔
501
                    uuid: Uuid::new_v4().as_u128(),
1✔
502
                    name: "test".to_string(),
1✔
503
                }
1✔
504
                .into(),
1✔
505
                ty: Type::Duration,
1✔
506
                constant: false,
1✔
507
            }
1✔
508
            .into(),
1✔
509
            right: Binary {
1✔
510
                op: BinaryOp::Div,
1✔
511
                left: Stretch {
1✔
512
                    uuid: Uuid::new_v4().as_u128(),
1✔
513
                    name: "test".to_string(),
1✔
514
                }
1✔
515
                .into(),
1✔
516
                right: Value::Duration(Duration::dt(1000)).into(),
1✔
517
                ty: Type::Float,
1✔
518
                constant: true,
1✔
519
            }
1✔
520
            .into(),
1✔
521
            ty: Type::Bool,
1✔
522
            constant: false,
1✔
523
        }
1✔
524
        .into();
1✔
525

1✔
526
        let exprs: Vec<ExprRef> = expr.iter().collect();
1✔
527
        assert!(matches!(
1✔
528
            exprs.as_slice(),
1✔
529
            [
1✔
530
                ExprRef::Binary(Binary {
1✔
531
                    op: BinaryOp::Mul,
1✔
532
                    ..
1✔
533
                }),
1✔
534
                ExprRef::Binary(Binary {
1✔
535
                    op: BinaryOp::Div,
1✔
536
                    ..
1✔
537
                }),
1✔
538
                ExprRef::Value(Value::Duration(..)),
1✔
539
                ExprRef::Stretch(Stretch { .. }),
1✔
540
                ExprRef::Binary(Binary {
1✔
541
                    op: BinaryOp::Add,
1✔
542
                    ..
1✔
543
                }),
1✔
544
                ExprRef::Stretch(Stretch { .. }),
1✔
545
                ExprRef::Var(Var::Standalone { .. }),
1✔
546
            ]
1✔
547
        ));
548
    }
1✔
549

550
    #[test]
551
    fn test_visit_mut_ordering() -> PyResult<()> {
1✔
552
        let mut expr: Expr = Binary {
1✔
553
            op: BinaryOp::Mul,
1✔
554
            left: Binary {
1✔
555
                op: BinaryOp::Add,
1✔
556
                left: Var::Standalone {
1✔
557
                    uuid: Uuid::new_v4().as_u128(),
1✔
558
                    name: "test".to_string(),
1✔
559
                    ty: Type::Duration,
1✔
560
                }
1✔
561
                .into(),
1✔
562
                right: Stretch {
1✔
563
                    uuid: Uuid::new_v4().as_u128(),
1✔
564
                    name: "test".to_string(),
1✔
565
                }
1✔
566
                .into(),
1✔
567
                ty: Type::Duration,
1✔
568
                constant: false,
1✔
569
            }
1✔
570
            .into(),
1✔
571
            right: Binary {
1✔
572
                op: BinaryOp::Div,
1✔
573
                left: Stretch {
1✔
574
                    uuid: Uuid::new_v4().as_u128(),
1✔
575
                    name: "test".to_string(),
1✔
576
                }
1✔
577
                .into(),
1✔
578
                right: Value::Duration(Duration::dt(1000)).into(),
1✔
579
                ty: Type::Float,
1✔
580
                constant: true,
1✔
581
            }
1✔
582
            .into(),
1✔
583
            ty: Type::Bool,
1✔
584
            constant: false,
1✔
585
        }
1✔
586
        .into();
1✔
587

1✔
588
        // These get *consumed* by every visit, so by the end we expect this
1✔
589
        // iterator to be empty. The ordering here is post-order, LRN.
1✔
590
        let mut order = [
1✔
591
            |x: &ExprRefMut| matches!(x, ExprRefMut::Var(Var::Standalone { .. })),
1✔
592
            |x: &ExprRefMut| matches!(x, ExprRefMut::Stretch(Stretch { .. })),
1✔
593
            |x: &ExprRefMut| {
1✔
NEW
594
                matches!(
×
595
                    x,
1✔
596
                    ExprRefMut::Binary(Binary {
597
                        op: BinaryOp::Add,
598
                        ..
599
                    })
600
                )
601
            },
1✔
602
            |x: &ExprRefMut| matches!(x, ExprRefMut::Stretch(Stretch { .. })),
1✔
603
            |x: &ExprRefMut| matches!(x, ExprRefMut::Value(Value::Duration(..))),
1✔
604
            |x: &ExprRefMut| {
1✔
NEW
605
                matches!(
×
606
                    x,
1✔
607
                    ExprRefMut::Binary(Binary {
608
                        op: BinaryOp::Div,
609
                        ..
610
                    })
611
                )
612
            },
1✔
613
            |x: &ExprRefMut| {
1✔
NEW
614
                matches!(
×
615
                    x,
1✔
616
                    ExprRefMut::Binary(Binary {
617
                        op: BinaryOp::Mul,
618
                        ..
619
                    })
620
                )
621
            },
1✔
622
        ]
1✔
623
        .into_iter();
1✔
624

1✔
625
        expr.visit_mut(|x| {
7✔
626
            assert!(order.next().unwrap()(&x));
7✔
627
            Ok(())
7✔
628
        })?;
7✔
629

630
        assert!(order.next().is_none());
1✔
631
        Ok(())
1✔
632
    }
1✔
633

634
    #[test]
635
    fn test_visit_mut() -> PyResult<()> {
1✔
636
        let mut expr: Expr = Binary {
1✔
637
            op: BinaryOp::BitAnd,
1✔
638
            left: Unary {
1✔
639
                op: UnaryOp::BitNot,
1✔
640
                operand: Var::Standalone {
1✔
641
                    uuid: Uuid::new_v4().as_u128(),
1✔
642
                    name: "test".to_string(),
1✔
643
                    ty: Type::Bool,
1✔
644
                }
1✔
645
                .into(),
1✔
646
                ty: Type::Bool,
1✔
647
                constant: false,
1✔
648
            }
1✔
649
            .into(),
1✔
650
            right: Value::Uint {
1✔
651
                raw: 1,
1✔
652
                ty: Type::Bool,
1✔
653
            }
1✔
654
            .into(),
1✔
655
            ty: Type::Bool,
1✔
656
            constant: false,
1✔
657
        }
1✔
658
        .into();
1✔
659

1✔
660
        expr.visit_mut(|x| match x {
1✔
661
            ExprRefMut::Var(Var::Standalone { name, .. }) => {
1✔
662
                *name = "updated".to_string();
1✔
663
                Ok(())
1✔
664
            }
665
            _ => Ok(()),
3✔
666
        })?;
4✔
667

668
        let Var::Standalone { name, .. } = expr.vars().next().unwrap() else {
1✔
NEW
669
            panic!("wrong var type")
×
670
        };
671
        assert_eq!(name.as_str(), "updated");
1✔
672
        Ok(())
1✔
673
    }
1✔
674
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc