Skip to main content

miden_assembly_syntax/ast/
op.rs

1use core::fmt;
2
3use miden_debug_types::{SourceSpan, Span, Spanned};
4
5use super::{Block, Immediate, Instruction};
6
7/// Represents the Miden Assembly instruction set syntax
8///
9/// This is separate from [Instruction] in order to distinguish control flow instructions and
10/// instructions with block regions from the rest.
11#[derive(Clone)]
12#[repr(u8)]
13pub enum Op {
14    /// Represents a conditional branch
15    ///
16    /// Can be either `if`..`end`, or `if`..`else`..`end`.
17    If {
18        span: SourceSpan,
19        /// This block is always present and non-empty
20        then_blk: Block,
21        /// This block will be empty if no `else` branch was present
22        else_blk: Block,
23    } = 0,
24    /// Represents a head-controlled loop (`while.true`..`end`).
25    While { span: SourceSpan, body: Block } = 1,
26    /// Represents a tail-controlled loop (`do`..`while`..`end`).
27    ///
28    /// The `body` always executes at least once; the `condition` block is then evaluated and must
29    /// leave a boolean on top of the stack. The loop re-iterates while that boolean is `1` and
30    /// exits when it is `0`.
31    DoWhile {
32        span: SourceSpan,
33        body: Block,
34        condition: Block,
35    } = 4,
36    /// Represents a counter-controlled loop.
37    ///
38    /// NOTE: The iteration count must be known at compile-time, so this is _not_ used for general
39    /// `for`-style loops where the iteration count is dynamic.
40    /// Also, it may be either a literal value or constant name (e.g. `add.1` or `add.CONST`)
41    Repeat {
42        span: SourceSpan,
43        count: Immediate<u32>,
44        body: Block,
45    } = 2,
46    /// A primitive operation, e.g. `add`
47    Inst(Span<Instruction>) = 3,
48}
49
50impl crate::prettier::PrettyPrint for Op {
51    fn render(&self) -> crate::prettier::Document {
52        use crate::prettier::*;
53
54        match self {
55            Self::If { then_blk, else_blk, .. } => {
56                text("if.true") + then_blk.render() + text("else") + else_blk.render() + text("end")
57            },
58            Self::While { body, .. } => text("while.true") + body.render() + text("end"),
59            Self::DoWhile { body, condition, .. } => {
60                text("do") + body.render() + text("while") + condition.render() + text("end")
61            },
62            Self::Repeat { count, body, .. } => {
63                display(format!("repeat.{count}")) + body.render() + text("end")
64            },
65            Self::Inst(inst) => inst.render(),
66        }
67    }
68}
69
70impl fmt::Debug for Op {
71    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
72        match self {
73            Self::If { then_blk, else_blk, .. } => {
74                f.debug_struct("If").field("then", then_blk).field("else", else_blk).finish()
75            },
76            Self::While { body, .. } => f.debug_tuple("While").field(body).finish(),
77            Self::DoWhile { body, condition, .. } => f
78                .debug_struct("DoWhile")
79                .field("body", body)
80                .field("condition", condition)
81                .finish(),
82            Self::Repeat { count, body, .. } => {
83                f.debug_struct("Repeat").field("count", count).field("body", body).finish()
84            },
85            Self::Inst(inst) => fmt::Debug::fmt(&**inst, f),
86        }
87    }
88}
89
90impl Eq for Op {}
91
92impl PartialEq for Op {
93    fn eq(&self, other: &Self) -> bool {
94        match (self, other) {
95            (
96                Self::If { then_blk: lt, else_blk: le, .. },
97                Self::If { then_blk: rt, else_blk: re, .. },
98            ) => lt == rt && le == re,
99            (Self::While { body: lbody, .. }, Self::While { body: rbody, .. }) => lbody == rbody,
100            (
101                Self::DoWhile { body: lbody, condition: lcond, .. },
102                Self::DoWhile { body: rbody, condition: rcond, .. },
103            ) => lbody == rbody && lcond == rcond,
104            (
105                Self::Repeat { count: lcount, body: lbody, .. },
106                Self::Repeat { count: rcount, body: rbody, .. },
107            ) => lcount == rcount && lbody == rbody,
108            (Self::Inst(l), Self::Inst(r)) => l == r,
109            _ => false,
110        }
111    }
112}
113
114impl Spanned for Op {
115    fn span(&self) -> SourceSpan {
116        match self {
117            Self::If { span, .. }
118            | Self::While { span, .. }
119            | Self::DoWhile { span, .. }
120            | Self::Repeat { span, .. } => *span,
121            Self::Inst(spanned) => spanned.span(),
122        }
123    }
124}