ram_machine/parser/
mod.rs1pub 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}