ram_machine/parser/
mod.rs

1pub mod instruction;
2pub mod operand;
3
4use crate::{instruction::Instruction, instruction::InstructionParseError};
5use std::{collections::HashMap, str::FromStr};
6use thiserror::Error;
7
8use super::operand::CellAddress;
9
10#[derive(Debug, PartialEq, Eq, Clone)]
11pub struct RamCode {
12    pub instructions: Vec<Instruction>,
13    pub jump_table: HashMap<String, CellAddress>,
14}
15
16#[derive(Error, Debug, PartialEq, Eq)]
17pub enum CodeParseError {
18    #[error("Expected EOL, found `{0}`")]
19    UnexpectedArgument(String),
20    #[error(transparent)]
21    InstructionParseError(#[from] InstructionParseError),
22}
23
24macro_rules! return_if_comment {
25    ($e:expr) => {
26        if $e.starts_with('#') {
27            return Ok(());
28        }
29    };
30}
31
32const LABEL_END: char = ':';
33
34impl RamCode {
35    pub fn new() -> RamCode {
36        RamCode {
37            instructions: Vec::new(),
38            jump_table: HashMap::new(),
39        }
40    }
41
42    pub fn push_line(&mut self, line: &str) -> Result<(), CodeParseError> {
43        let mut slices = line.split_whitespace().filter(|s| !s.is_empty());
44
45        let mut slice = match slices.next() {
46            None => return Ok(()),
47            Some(val) => val,
48        };
49
50        return_if_comment!(slice);
51
52        if slice.ends_with(LABEL_END) {
53            self.jump_table.insert(
54                slice.trim_end_matches(':').to_owned(),
55                self.instructions.len(),
56            );
57
58            slice = match slices.next() {
59                Some(val) => val,
60                None => return Ok(()),
61            }
62        }
63
64        return_if_comment!(slice);
65
66        let argument = slices.next();
67
68        let instruction = Instruction::try_from((slice, argument))?;
69        self.add_instruction(instruction);
70
71        let rest = slices.next();
72
73        if let Some(v) = rest {
74            return_if_comment!(v);
75            return Err(CodeParseError::UnexpectedArgument(v.to_string()));
76        }
77
78        Ok(())
79    }
80
81    pub fn add_instruction(&mut self, instruction: Instruction) {
82        self.instructions.push(instruction)
83    }
84}
85
86impl FromStr for RamCode {
87    type Err = CodeParseError;
88    fn from_str(s: &str) -> Result<Self, Self::Err> {
89        let mut code = RamCode::new();
90        let lines = s.lines().filter(|line| !line.is_empty());
91        for line in lines {
92            code.push_line(line)?;
93        }
94        Ok(code)
95    }
96}