/ synthesizer / program / src / logic / command / branch.rs
branch.rs
  1  // Copyright (c) 2025 ADnet Contributors
  2  // This file is part of the AlphaVM 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  
 16  use crate::{Opcode, Operand};
 17  use console::{network::prelude::*, program::Identifier};
 18  
 19  /// Jumps to `position`, if `first` equals `second`.
 20  pub type BranchEq<N> = Branch<N, { Variant::BranchEq as u8 }>;
 21  /// Jumps to `position`, if `first` does **not** equal `second`.
 22  pub type BranchNeq<N> = Branch<N, { Variant::BranchNeq as u8 }>;
 23  
 24  enum 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)]
 31  pub struct Branch<N: Network, const VARIANT: u8> {
 32      /// The operands.
 33      operands: [Operand<N>; 2],
 34      /// The position.
 35      position: Identifier<N>,
 36  }
 37  
 38  impl<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  
 74  impl<N: Network, const VARIANT: u8> Parser for Branch<N, VARIANT> {
 75      /// Parses a string into an command.
 76      #[inline]
 77      fn parse(string: &str) -> ParserResult<Self> {
 78          // Parse the whitespace and comments from the string.
 79          let (string, _) = Sanitizer::parse(string)?;
 80          // Parse the opcode from the string.
 81          let (string, _) = tag(*Self::opcode())(string)?;
 82          // Parse the whitespace from the string.
 83          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 84  
 85          // Parse the first operand from the string.
 86          let (string, first) = Operand::parse(string)?;
 87          // Parse the whitespace from the string.
 88          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 89  
 90          // Parse the second operand from the string.
 91          let (string, second) = Operand::parse(string)?;
 92          // Parse the whitespace from the string.
 93          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 94  
 95          // Parse the "to" from the string.
 96          let (string, _) = tag("to")(string)?;
 97          // Parse the whitespace from the string.
 98          let (string, _) = Sanitizer::parse_whitespaces(string)?;
 99          // Parse the position from the string.
100          let (string, position) = Identifier::parse(string)?;
101  
102          // Parse the whitespace from the string.
103          let (string, _) = Sanitizer::parse_whitespaces(string)?;
104          // Parse the ";" from the string.
105          let (string, _) = tag(";")(string)?;
106  
107          Ok((string, Self { operands: [first, second], position }))
108      }
109  }
110  
111  impl<N: Network, const VARIANT: u8> FromStr for Branch<N, VARIANT> {
112      type Err = Error;
113  
114      /// Parses a string into a command.
115      #[inline]
116      fn from_str(string: &str) -> Result<Self> {
117          match Self::parse(string) {
118              Ok((remainder, object)) => {
119                  // Ensure the remainder is empty.
120                  ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
121                  // Return the object.
122                  Ok(object)
123              }
124              Err(error) => bail!("Failed to parse string. {error}"),
125          }
126      }
127  }
128  
129  impl<N: Network, const VARIANT: u8> Debug for Branch<N, VARIANT> {
130      /// Prints the command as a string.
131      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
132          Display::fmt(self, f)
133      }
134  }
135  
136  impl<N: Network, const VARIANT: u8> Display for Branch<N, VARIANT> {
137      /// Prints the command to a string.
138      fn fmt(&self, f: &mut Formatter) -> fmt::Result {
139          // Print the command.
140          write!(f, "{} {} {} to {};", Self::opcode(), self.first(), self.second(), self.position)
141      }
142  }
143  
144  impl<N: Network, const VARIANT: u8> FromBytes for Branch<N, VARIANT> {
145      /// Reads the command from a buffer.
146      fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
147          // Read the first operand.
148          let first = Operand::read_le(&mut reader)?;
149          // Read the second operand.
150          let second = Operand::read_le(&mut reader)?;
151          // Read the position.
152          let position = Identifier::read_le(&mut reader)?;
153  
154          // Return the command.
155          Ok(Self { operands: [first, second], position })
156      }
157  }
158  
159  impl<N: Network, const VARIANT: u8> ToBytes for Branch<N, VARIANT> {
160      /// Writes the command to a buffer.
161      fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
162          // Write the first operand.
163          self.first().write_le(&mut writer)?;
164          // Write the second operand.
165          self.second().write_le(&mut writer)?;
166          // Write the position.
167          self.position.write_le(&mut writer)
168      }
169  }
170  
171  #[cfg(test)]
172  mod tests {
173      use super::*;
174      use console::{
175          network::MainnetV0,
176          program::{Identifier, Register},
177      };
178  
179      type CurrentNetwork = MainnetV0;
180  
181      #[test]
182      fn test_parse() {
183          let (string, branch) = BranchEq::<CurrentNetwork>::parse("branch.eq r0 r1 to exit;").unwrap();
184          assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
185          assert_eq!(branch.first(), &Operand::Register(Register::Locator(0)), "The first operand is incorrect");
186          assert_eq!(branch.second(), &Operand::Register(Register::Locator(1)), "The second operand is incorrect");
187          assert_eq!(branch.position, Identifier::from_str("exit").unwrap(), "The position is incorrect");
188  
189          let (string, branch) = BranchNeq::<CurrentNetwork>::parse("branch.neq r3 r4 to start;").unwrap();
190          assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
191          assert_eq!(branch.first(), &Operand::Register(Register::Locator(3)), "The first operand is incorrect");
192          assert_eq!(branch.second(), &Operand::Register(Register::Locator(4)), "The second operand is incorrect");
193          assert_eq!(branch.position, Identifier::from_str("start").unwrap(), "The position is incorrect");
194      }
195  }