1use std::fmt;
2
3use crate::operand::{CellOperand, Operand, OperandParseError};
4use thiserror::Error;
5
6#[derive(Debug, Clone, PartialEq, Eq)]
7pub enum Instruction {
8 Load(Operand),
9 Store(CellOperand),
10 Add(Operand),
11 Sub(Operand),
12 Mult(Operand),
13 Div(Operand),
14 Read(CellOperand),
15 Write(Operand),
16 Jump(String),
17 Jgtz(String),
18 Jzero(String),
19 Halt,
20}
21
22#[derive(Error, Debug, PartialEq, Eq)]
23pub enum InstructionParseError {
24 #[error("Expected a label after keyword {0}, got nothing")]
25 LabelNotFound(String),
26 #[error("Keyword `{0}` is not a valid keyword")]
27 InvalidKeyword(String),
28 #[error(transparent)]
29 OperandParseError(#[from] OperandParseError),
30 #[error("Expected nothing, found `{0}`")]
31 UnexpectedArgument(String),
32}
33
34fn parse_label(keyword: &str, s: Option<&str>) -> Result<String, InstructionParseError> {
35 match s {
36 Some(v) => Ok(v.to_owned()),
37 None => Err(InstructionParseError::LabelNotFound(keyword.to_owned())),
38 }
39}
40
41impl TryFrom<(&str, Option<&str>)> for Instruction {
42 type Error = InstructionParseError;
43 fn try_from((keyword, argument): (&str, Option<&str>)) -> Result<Self, Self::Error> {
44 match keyword.to_lowercase().as_str() {
45 "load" => Ok(Self::Load(Operand::try_from((argument, keyword))?)),
46 "store" => Ok(Self::Store(CellOperand::try_from((argument, keyword))?)),
47 "add" => Ok(Self::Add(Operand::try_from((argument, keyword))?)),
48 "sub" => Ok(Self::Sub(Operand::try_from((argument, keyword))?)),
49 "mult" => Ok(Self::Mult(Operand::try_from((argument, keyword))?)),
50 "div" => Ok(Self::Div(Operand::try_from((argument, keyword))?)),
51 "read" => Ok(Self::Read(CellOperand::try_from((argument, keyword))?)),
52 "write" => Ok(Self::Write(Operand::try_from((argument, keyword))?)),
53 "jump" => Ok(Self::Jump(parse_label(keyword, argument)?)),
54 "jgtz" => Ok(Self::Jgtz(parse_label(keyword, argument)?)),
55 "jzero" => Ok(Self::Jzero(parse_label(keyword, argument)?)),
56 "halt" => {
57 if let Some(v) = argument {
58 Err(InstructionParseError::UnexpectedArgument(v.to_owned()))
59 } else {
60 Ok(Self::Halt)
61 }
62 }
63 _ => Err(InstructionParseError::InvalidKeyword(keyword.to_owned())),
64 }
65 }
66}
67
68impl fmt::Display for Instruction {
69 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
70 match self {
71 Instruction::Load(op) => write!(f, "LOAD {}", op),
72 Instruction::Store(cell_op) => write!(f, "STORE {}", cell_op),
73 Instruction::Add(op) => write!(f, "ADD {}", op),
74 Instruction::Sub(op) => write!(f, "SUB {}", op),
75 Instruction::Mult(op) => write!(f, "MULT {}", op),
76 Instruction::Div(op) => write!(f, "DIV {}", op),
77 Instruction::Read(cell_op) => write!(f, "READ {}", cell_op),
78 Instruction::Write(op) => write!(f, "WRITE {}", op),
79 Instruction::Jump(label) => write!(f, "JUMP {}", label),
80 Instruction::Jgtz(label) => write!(f, "JGTZ {}", label),
81 Instruction::Jzero(label) => write!(f, "JZERO {}", label),
82 Instruction::Halt => write!(f, "HALT"),
83 }
84 }
85}