1#![forbid(unsafe_code)]
2
3use std::{fmt::Debug, hash::Hash, str::FromStr};
4
5use lc3b_isa::{AddInstruction, Immediate5, Instruction, Register};
6use pest::{
7 iterators::{Pair, Pairs},
8 Parser,
9};
10
11#[derive(pest_derive::Parser)]
12#[grammar = "lc3b_asm.pest"]
13struct LC3BAsmParser {}
14
15pub type Error = pest::error::Error<Rule>;
16
17pub fn parse_to_pairs(program: &str) -> Result<Pairs<Rule>, Box<Error>> {
18 LC3BAsmParser::parse(Rule::program, program).map_err(Box::new)
19}
20
21pub fn parse_to_program(program: &str) -> eyre::Result<Vec<Instruction>> {
22 let program = LC3BAsmParser::parse(Rule::program, program)?
23 .next()
24 .unwrap();
25
26 let instructions = program.into_inner();
27
28 let instructions = instructions
29 .filter_map(|i| match i.as_rule() {
30 Rule::instruction => Some(instruction_from_pair(i)),
31 Rule::comment => None,
32 other => unimplemented!("don't handle {:#?}", other),
33 })
34 .collect::<Vec<_>>();
35
36 if instructions.iter().any(Result::is_err) {
37 return Err(eyre::eyre!(
38 "there was an error parsing the program: {:#?}",
39 instructions
40 ));
41 }
42
43 let instructions = instructions.into_iter().flatten().collect();
44
45 Ok(instructions)
46}
47
48fn instruction_from_pair(pair: Pair<Rule>) -> eyre::Result<Instruction> {
49 let mut inner = pair.into_inner();
50 let opcode = inner.next();
51 if opcode.is_none() {
52 return Err(eyre::eyre!("could not handle {:#?}", opcode));
53 }
54 let opcode = opcode.unwrap();
55
56 let instruction = match opcode.as_str() {
57 "ADD" => {
58 let mut operands = inner.next().unwrap().into_inner();
59 let arg_one = operands.next().unwrap().as_str();
60 let dst_reg = Register::from_str(arg_one)?;
61
62 let arg_two = operands.next().unwrap().as_str();
63 let src_reg = Register::from_str(arg_two)?;
64
65 let arg_three = operands.next().unwrap();
66 let inner: AddInstruction = match arg_three.as_rule() {
67 Rule::literal => {
68 let imm5 = Immediate5::from_str(arg_three.as_str())?;
69 AddInstruction::AddImm(dst_reg, src_reg, imm5)
70 }
71 Rule::register => {
72 let src2_reg = Register::from_str(arg_three.as_str())?;
73 AddInstruction::AddReg(src_reg, dst_reg, src2_reg)
74 }
75 _ => return Err(eyre::eyre!("unhandled `{:?}`", arg_three)),
76 };
77 Instruction::AddInstruction(inner)
78 }
79 other => panic!("unhandled opcode {:#?}", other),
80 };
81
82 Ok(instruction)
83}
84
85#[cfg(test)]
86mod test {
87 use lc3b_isa::{AddInstruction, Immediate5, Instruction, Register};
88
89 #[test]
90 pub fn stuff() {
91 let test_asm = r#"
92 ADD R1, R1, 8; this is a comment
93 ADD R1, R2, 10;
94"#;
95
96 let instructions = super::parse_to_program(test_asm).unwrap();
97 assert_eq!(
98 instructions,
99 [
100 Instruction::AddInstruction(AddInstruction::AddImm(
101 Register::Register1,
102 Register::Register1,
103 Immediate5::new(8,).unwrap(),
104 ),),
105 Instruction::AddInstruction(AddInstruction::AddImm(
106 Register::Register1,
107 Register::Register2,
108 Immediate5::new(10,).unwrap(),
109 ),),
110 ]
111 )
112 }
113}