use mlem::{Instruction, Program};
use super::lex;
mod address;
use self::address::parse_address;
mod instruction;
use self::instruction::{InstructionName, parse_instruction};
#[cfg(test)]
mod test;
pub fn parse_line(line: &str) -> Result<Option<Instruction>, String> {
let pieces: Vec<_> = lex::lex_line(line);
if pieces.len() == 0 { return Ok(None); }
let mut instruction_name = InstructionName::None;
let mut arg1 = None;
let mut arg2 = None;
let mut arg3 = None;
if pieces.len() >= 1 {
match parse_instruction(pieces[0]) {
Ok(v) => { instruction_name = v; },
Err(e) => { return Err(e); }
};
}
if pieces.len() == 1 {
return match instruction_name {
InstructionName::NoOp => Ok(Some(Instruction::NoOp)),
InstructionName::Halt => Ok(Some(Instruction::Halt)),
InstructionName::Illegal => Ok(Some(Instruction::Illegal)),
_ => Err("Wrong number of arguments. Got 0.".into())
};
}
if pieces.len() >= 2 {
match parse_address(pieces[1].trim()) {
Ok(v) => { arg1 = Some(v); },
Err(e) => { return Err(e); }
};
}
if pieces.len() >= 3 {
match parse_address(pieces[2].trim()) {
Ok(v) => { arg2 = Some(v); },
Err(e) => { return Err(e); }
};
}
if pieces.len() >= 4 {
match parse_address(pieces[3].trim()) {
Ok(v) => { arg3 = Some(v); },
Err(e) => { return Err(e); }
};
}
if pieces.len() == 2 {
let arg1 = arg1.unwrap();
return match instruction_name {
InstructionName::Zero => Ok(Some(Instruction::Zero(arg1))),
InstructionName::Input => Ok(Some(Instruction::Input(arg1))),
InstructionName::Output => Ok(Some(Instruction::Output(arg1))),
InstructionName::Jump => Ok(Some(Instruction::Jump(arg1))),
InstructionName::Push => Ok(Some(Instruction::Push(arg1))),
InstructionName::Pop => Ok(Some(Instruction::Pop(arg1))),
_ => Err("Wrong number of arguments. Got 1.".into())
};
}
if pieces.len() == 3 {
let arg1 = arg1.unwrap();
let arg2 = arg2.unwrap();
return match instruction_name {
InstructionName::Move => Ok(Some(Instruction::Move(arg1, arg2))),
InstructionName::Add => Ok(Some(Instruction::Add(arg1, arg2))),
InstructionName::Sub => Ok(Some(Instruction::Sub(arg1, arg2))),
InstructionName::JumpIfZero => Ok(Some(Instruction::JumpIfZero(arg1, arg2))),
InstructionName::JumpNotZero => Ok(Some(Instruction::JumpNotZero(arg1, arg2))),
_ => Err("Wrong number of arguments. Got 2.".into())
}
}
Err("Malformed. Perhaps there are too many terms?".into())
}
fn initial_parse_program(program: &str) -> Vec<Result<Option<Instruction>, String>> {
let lines = program.lines();
let mut v = Vec::new();
for line in lines {
match parse_line(line) {
Ok(i) => { v.push(Ok(i)); }
Err(e) => { v.push(Err(e)); }
}
}
v
}
pub fn parse_program(program: &str) -> Result<Program, Vec<(u64, String)>> {
let mut p = Vec::new();
let mut errors = Vec::new();
for (n, line) in initial_parse_program(program).into_iter().enumerate() {
match line {
Ok(v) => {
if let Some(i) = v { p.push(i) };
},
Err(e) => {
errors.push((n as u64, format!("{}", e)));
}
};
}
if errors.len() == 0 {
Ok(p)
} else {
Err(errors)
}
}