Skip to main content

snarkvm_synthesizer_program/logic/command/
branch.rs

1// Copyright (c) 2019-2025 Provable Inc.
2// This file is part of the snarkVM library.
3
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at:
7
8// http://www.apache.org/licenses/LICENSE-2.0
9
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16use crate::{Opcode, Operand};
17use console::{network::prelude::*, program::Identifier};
18
19/// Jumps to `position`, if `first` equals `second`.
20pub type BranchEq<N> = Branch<N, { Variant::BranchEq as u8 }>;
21/// Jumps to `position`, if `first` does **not** equal `second`.
22pub type BranchNeq<N> = Branch<N, { Variant::BranchNeq as u8 }>;
23
24enum Variant {
25    BranchEq,
26    BranchNeq,
27}
28
29/// Compares `first` and `second` and jumps to `position`, if the condition is met.
30#[derive(Clone, PartialEq, Eq, Hash)]
31pub struct Branch<N: Network, const VARIANT: u8> {
32    /// The operands.
33    operands: [Operand<N>; 2],
34    /// The position.
35    position: Identifier<N>,
36}
37
38impl<N: Network, const VARIANT: u8> Branch<N, VARIANT> {
39    /// Returns the opcode.
40    #[inline]
41    pub const fn opcode() -> Opcode {
42        match VARIANT {
43            0 => Opcode::Command("branch.eq"),
44            1 => Opcode::Command("branch.neq"),
45            _ => panic!("Invalid 'branch' instruction opcode"),
46        }
47    }
48
49    /// Returns the operands.
50    #[inline]
51    pub const fn operands(&self) -> &[Operand<N>] {
52        &self.operands
53    }
54
55    /// Returns the first operand.
56    #[inline]
57    pub const fn first(&self) -> &Operand<N> {
58        &self.operands[0]
59    }
60
61    /// Returns the second operand.
62    #[inline]
63    pub const fn second(&self) -> &Operand<N> {
64        &self.operands[1]
65    }
66
67    /// Returns the position.
68    #[inline]
69    pub const fn position(&self) -> &Identifier<N> {
70        &self.position
71    }
72
73    /// Returns whether this command refers to an external struct.
74    #[inline]
75    pub fn contains_external_struct(&self) -> bool {
76        false
77    }
78}
79
80impl<N: Network, const VARIANT: u8> Parser for Branch<N, VARIANT> {
81    /// Parses a string into an command.
82    #[inline]
83    fn parse(string: &str) -> ParserResult<Self> {
84        // Parse the whitespace and comments from the string.
85        let (string, _) = Sanitizer::parse(string)?;
86        // Parse the opcode from the string.
87        let (string, _) = tag(*Self::opcode())(string)?;
88        // Parse the whitespace from the string.
89        let (string, _) = Sanitizer::parse_whitespaces(string)?;
90
91        // Parse the first operand from the string.
92        let (string, first) = Operand::parse(string)?;
93        // Parse the whitespace from the string.
94        let (string, _) = Sanitizer::parse_whitespaces(string)?;
95
96        // Parse the second operand from the string.
97        let (string, second) = Operand::parse(string)?;
98        // Parse the whitespace from the string.
99        let (string, _) = Sanitizer::parse_whitespaces(string)?;
100
101        // Parse the "to" from the string.
102        let (string, _) = tag("to")(string)?;
103        // Parse the whitespace from the string.
104        let (string, _) = Sanitizer::parse_whitespaces(string)?;
105        // Parse the position from the string.
106        let (string, position) = Identifier::parse(string)?;
107
108        // Parse the whitespace from the string.
109        let (string, _) = Sanitizer::parse_whitespaces(string)?;
110        // Parse the ";" from the string.
111        let (string, _) = tag(";")(string)?;
112
113        Ok((string, Self { operands: [first, second], position }))
114    }
115}
116
117impl<N: Network, const VARIANT: u8> FromStr for Branch<N, VARIANT> {
118    type Err = Error;
119
120    /// Parses a string into a command.
121    #[inline]
122    fn from_str(string: &str) -> Result<Self> {
123        match Self::parse(string) {
124            Ok((remainder, object)) => {
125                // Ensure the remainder is empty.
126                ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
127                // Return the object.
128                Ok(object)
129            }
130            Err(error) => bail!("Failed to parse string. {error}"),
131        }
132    }
133}
134
135impl<N: Network, const VARIANT: u8> Debug for Branch<N, VARIANT> {
136    /// Prints the command as a string.
137    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
138        Display::fmt(self, f)
139    }
140}
141
142impl<N: Network, const VARIANT: u8> Display for Branch<N, VARIANT> {
143    /// Prints the command to a string.
144    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
145        // Print the command.
146        write!(f, "{} {} {} to {};", Self::opcode(), self.first(), self.second(), self.position)
147    }
148}
149
150impl<N: Network, const VARIANT: u8> FromBytes for Branch<N, VARIANT> {
151    /// Reads the command from a buffer.
152    fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
153        // Read the first operand.
154        let first = Operand::read_le(&mut reader)?;
155        // Read the second operand.
156        let second = Operand::read_le(&mut reader)?;
157        // Read the position.
158        let position = Identifier::read_le(&mut reader)?;
159
160        // Return the command.
161        Ok(Self { operands: [first, second], position })
162    }
163}
164
165impl<N: Network, const VARIANT: u8> ToBytes for Branch<N, VARIANT> {
166    /// Writes the command to a buffer.
167    fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
168        // Write the first operand.
169        self.first().write_le(&mut writer)?;
170        // Write the second operand.
171        self.second().write_le(&mut writer)?;
172        // Write the position.
173        self.position.write_le(&mut writer)
174    }
175}
176
177#[cfg(test)]
178mod tests {
179    use super::*;
180    use console::{
181        network::MainnetV0,
182        program::{Identifier, Register},
183    };
184
185    type CurrentNetwork = MainnetV0;
186
187    #[test]
188    fn test_parse() {
189        let (string, branch) = BranchEq::<CurrentNetwork>::parse("branch.eq r0 r1 to exit;").unwrap();
190        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
191        assert_eq!(branch.first(), &Operand::Register(Register::Locator(0)), "The first operand is incorrect");
192        assert_eq!(branch.second(), &Operand::Register(Register::Locator(1)), "The second operand is incorrect");
193        assert_eq!(branch.position, Identifier::from_str("exit").unwrap(), "The position is incorrect");
194
195        let (string, branch) = BranchNeq::<CurrentNetwork>::parse("branch.neq r3 r4 to start;").unwrap();
196        assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
197        assert_eq!(branch.first(), &Operand::Register(Register::Locator(3)), "The first operand is incorrect");
198        assert_eq!(branch.second(), &Operand::Register(Register::Locator(4)), "The second operand is incorrect");
199        assert_eq!(branch.position, Identifier::from_str("start").unwrap(), "The position is incorrect");
200    }
201}