use super::{ Instruction, Address }; #[derive(Debug)] pub(crate) enum ArgumentError { UnknownInstruction, TooMany, TooFew, NotLocation, NotNumber, NotString, } impl Instruction { fn new_subleq(a: &Address, b: &Address, c: &Address) -> Instruction { if let Address::Constants(_) = c { panic!("Constants cannot be the third subleq argument!"); } Instruction { name: "subleq".to_string(), args: vec![a.clone(), b.clone(), c.clone()], } } fn check_args_len(&self, expected: usize) -> Result<(), ArgumentError> { if self.args.len() < expected { return Err(ArgumentError::TooFew); } else if self.args.len() > 3 { return Err(ArgumentError::TooMany); } Ok(()) } pub(crate) fn check_args(&self) -> Result<(), ArgumentError> { match &*self.name { "subleq" => { self.check_args_len(3)?; for arg in &self.args { if ! arg.is_location() { return Err(ArgumentError::NotLocation); } } }, "jmp" => { self.check_args_len(1)?; if ! self.args[0].is_location() { return Err(ArgumentError::NotLocation); } }, "add" => { self.check_args_len(2)?; }, "mov" => { self.check_args_len(2)?; if ! self.args[1].is_location() { return Err(ArgumentError::NotLocation); } }, "beq" => { self.check_args_len(2)?; if ! self.args[1].is_location() { return Err(ArgumentError::NotLocation); } }, _ => return Err(ArgumentError::UnknownInstruction), } Ok(()) } pub(crate) fn collapse(&mut self, next: usize) -> Vec { self.name = self.name.to_lowercase(); static Z: Address = Address::Memory(0); match &*self.name { "subleq" => vec![self.clone()], "jmp" => vec![ Instruction::new_subleq(&Z, &Z, &self.args[0]), ], "add" => vec![ Instruction::new_subleq(&self.args[0], &Z, &Address::Instruction(next + 1)), Instruction::new_subleq(&Z, &self.args[1], &Address::Instruction(next + 2)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 3)), ], "mov" => vec![ Instruction::new_subleq(&self.args[1], &self.args[1], &Address::Instruction(next + 1)), Instruction::new_subleq(&self.args[0], &Z, &Address::Instruction(next + 2)), Instruction::new_subleq(&Z, &self.args[1], &Address::Instruction(next + 3)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 4)), ], "beq" => vec![ Instruction::new_subleq(&self.args[0], &Z, &Address::Instruction(next + 2)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 4)), Instruction::new_subleq(&Z, &Z, &Address::Instruction(next + 3)), Instruction::new_subleq(&Z, &self.args[0], &self.args[1] ), ], _ => panic!("Unknown instruction {}!", self.name), } } pub(crate) fn collapse_len(&self) -> usize { match &*self.name { "subleq" | "jmp" => 1, "add" => 3, "mov" | "beq" => 4, _ => panic!("Unknown instruction {}!", self.name), } } }