Website Summary Files Commits Branches @commit @diff @stats
path: root / assembler / src / parser / instruction.rs
blob: cc0523f92acb02a095931ff47804a8b2bd5e160b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use super::{ Instruction, Address };

#[derive(Debug)]
pub(crate) enum ArgumentError {
    UnknownInstruction,
    TooMany,
    TooFew,
    NotLocation,
    NotNumber,
    NotString,
}

impl Instruction {
    fn new_subleq(a: &Address, b: &Address, c: &Address) -> Instruction {
        if let Address::Constants(_) = c {
            panic!("Constants cannot be the third subleq argument!");
        }
        Instruction {
            name: "subleq".to_string(),
            args: vec![a.clone(), b.clone(), c.clone()],
        }
    }

    fn check_args_len(&self, expected: usize) -> Result<(), ArgumentError> {
        if self.args.len() < expected {
            return Err(ArgumentError::TooFew);
        }
        else if self.args.len() > 3 {
            return Err(ArgumentError::TooMany);
        }
        Ok(())
    }

    pub(crate) fn check_args(&self) -> Result<(), ArgumentError> {
        match &*self.name {
            "subleq" => {
                self.check_args_len(3)?;

                for arg in &self.args {
                    if ! arg.is_location() {
                        return Err(ArgumentError::NotLocation);
                    }
                }
            },
            "jmp" => {
                self.check_args_len(1)?;

                if ! self.args[0].is_location() {
                    return Err(ArgumentError::NotLocation);
                }
            },
            "add" => {
                self.check_args_len(2)?;
            },
            "mov" => {
                self.check_args_len(2)?;

                if ! self.args[1].is_location() {
                    return Err(ArgumentError::NotLocation);
                }
            },
            "beq" => {
                self.check_args_len(2)?;

                if ! self.args[1].is_location() {
                    return Err(ArgumentError::NotLocation);
                }
            },
            _ => return Err(ArgumentError::UnknownInstruction),
        }
        Ok(())
    }

    pub(crate) fn collapse(&mut self, next: usize) -> Vec<Instruction> {
        self.name = self.name.to_lowercase();
        static Z: Address = Address::Memory(0);

        match &*self.name {
            "subleq" => vec![self.clone()],
            "jmp" => vec![
                Instruction::new_subleq(&Z, &Z, &self.args[0]),
            ],
            "add" => vec![
                Instruction::new_subleq(&self.args[0], &Z,            &Address::Instruction(next + 1)),
                Instruction::new_subleq(&Z,            &self.args[1], &Address::Instruction(next + 2)),
                Instruction::new_subleq(&Z,            &Z,            &Address::Instruction(next + 3)),
            ],
            "mov" => vec![
                Instruction::new_subleq(&self.args[1], &self.args[1], &Address::Instruction(next + 1)),
                Instruction::new_subleq(&self.args[0], &Z,            &Address::Instruction(next + 2)),
                Instruction::new_subleq(&Z,            &self.args[1], &Address::Instruction(next + 3)),
                Instruction::new_subleq(&Z,            &Z,            &Address::Instruction(next + 4)),
            ],
            "beq" => vec![
                Instruction::new_subleq(&self.args[0], &Z,            &Address::Instruction(next + 2)),
                Instruction::new_subleq(&Z,            &Z,            &Address::Instruction(next + 4)),
                Instruction::new_subleq(&Z,            &Z,            &Address::Instruction(next + 3)),
                Instruction::new_subleq(&Z,            &self.args[0], &self.args[1]                  ),
            ],
            _ => panic!("Unknown instruction {}!", self.name),
        }
    }

    pub(crate) fn collapse_len(&self) -> usize {
        match &*self.name {
            "subleq" | "jmp"
                => 1,
            "add"
                => 3,
            "mov" | "beq"
                => 4,
            _ => panic!("Unknown instruction {}!", self.name),
        }
    }
}