use crate::{Opcode, Operand};
use console::{network::prelude::*, program::Identifier};
pub type BranchEq<N> = Branch<N, { Variant::BranchEq as u8 }>;
pub type BranchNeq<N> = Branch<N, { Variant::BranchNeq as u8 }>;
enum Variant {
BranchEq,
BranchNeq,
}
#[derive(Clone, PartialEq, Eq, Hash)]
pub struct Branch<N: Network, const VARIANT: u8> {
operands: [Operand<N>; 2],
position: Identifier<N>,
}
impl<N: Network, const VARIANT: u8> Branch<N, VARIANT> {
#[inline]
pub const fn opcode() -> Opcode {
match VARIANT {
0 => Opcode::Command("branch.eq"),
1 => Opcode::Command("branch.neq"),
_ => panic!("Invalid 'branch' instruction opcode"),
}
}
#[inline]
pub const fn operands(&self) -> &[Operand<N>] {
&self.operands
}
#[inline]
pub const fn first(&self) -> &Operand<N> {
&self.operands[0]
}
#[inline]
pub const fn second(&self) -> &Operand<N> {
&self.operands[1]
}
#[inline]
pub const fn position(&self) -> &Identifier<N> {
&self.position
}
#[inline]
pub fn contains_external_struct(&self) -> bool {
false
}
}
impl<N: Network, const VARIANT: u8> Parser for Branch<N, VARIANT> {
#[inline]
fn parse(string: &str) -> ParserResult<Self> {
let (string, _) = Sanitizer::parse(string)?;
let (string, _) = tag(*Self::opcode())(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, first) = Operand::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, second) = Operand::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag("to")(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, position) = Identifier::parse(string)?;
let (string, _) = Sanitizer::parse_whitespaces(string)?;
let (string, _) = tag(";")(string)?;
Ok((string, Self { operands: [first, second], position }))
}
}
impl<N: Network, const VARIANT: u8> FromStr for Branch<N, VARIANT> {
type Err = Error;
#[inline]
fn from_str(string: &str) -> Result<Self> {
match Self::parse(string) {
Ok((remainder, object)) => {
ensure!(remainder.is_empty(), "Failed to parse string. Found invalid character in: \"{remainder}\"");
Ok(object)
}
Err(error) => bail!("Failed to parse string. {error}"),
}
}
}
impl<N: Network, const VARIANT: u8> Debug for Branch<N, VARIANT> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
Display::fmt(self, f)
}
}
impl<N: Network, const VARIANT: u8> Display for Branch<N, VARIANT> {
fn fmt(&self, f: &mut Formatter) -> fmt::Result {
write!(f, "{} {} {} to {};", Self::opcode(), self.first(), self.second(), self.position)
}
}
impl<N: Network, const VARIANT: u8> FromBytes for Branch<N, VARIANT> {
fn read_le<R: Read>(mut reader: R) -> IoResult<Self> {
let first = Operand::read_le(&mut reader)?;
let second = Operand::read_le(&mut reader)?;
let position = Identifier::read_le(&mut reader)?;
Ok(Self { operands: [first, second], position })
}
}
impl<N: Network, const VARIANT: u8> ToBytes for Branch<N, VARIANT> {
fn write_le<W: Write>(&self, mut writer: W) -> IoResult<()> {
self.first().write_le(&mut writer)?;
self.second().write_le(&mut writer)?;
self.position.write_le(&mut writer)
}
}
#[cfg(test)]
mod tests {
use super::*;
use console::{
network::MainnetV0,
program::{Identifier, Register},
};
type CurrentNetwork = MainnetV0;
#[test]
fn test_parse() {
let (string, branch) = BranchEq::<CurrentNetwork>::parse("branch.eq r0 r1 to exit;").unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
assert_eq!(branch.first(), &Operand::Register(Register::Locator(0)), "The first operand is incorrect");
assert_eq!(branch.second(), &Operand::Register(Register::Locator(1)), "The second operand is incorrect");
assert_eq!(branch.position, Identifier::from_str("exit").unwrap(), "The position is incorrect");
let (string, branch) = BranchNeq::<CurrentNetwork>::parse("branch.neq r3 r4 to start;").unwrap();
assert!(string.is_empty(), "Parser did not consume all of the string: '{string}'");
assert_eq!(branch.first(), &Operand::Register(Register::Locator(3)), "The first operand is incorrect");
assert_eq!(branch.second(), &Operand::Register(Register::Locator(4)), "The second operand is incorrect");
assert_eq!(branch.position, Identifier::from_str("start").unwrap(), "The position is incorrect");
}
}