lc3b_assembler/
lib.rs

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}