stack_vm/
instruction_table.rs

1//! An instruction table.
2//!
3//! Stores the instructions of your machine and allows them to be retrieved
4//! by name or op code.
5
6use crate::instruction::Instruction;
7use std::collections::HashMap;
8use std::fmt;
9
10/// The instruction table.
11///
12/// Implemented as a `HashMap` behind the scenes.
13#[derive(Debug, Default)]
14pub struct InstructionTable<T: fmt::Debug>(HashMap<usize, Instruction<T>>);
15
16impl<T: fmt::Debug> InstructionTable<T> {
17    /// Create a new empty instruction table.
18    pub fn new() -> InstructionTable<T> {
19        InstructionTable(HashMap::new())
20    }
21
22    /// Retrieve an instruction by looking up it's op code.
23    pub fn by_op_code(&self, op_code: usize) -> Option<&Instruction<T>> {
24        self.0.get(&op_code)
25    }
26
27    /// Retrieve an instruction by looking up it's name.
28    pub fn by_name(&self, name: &str) -> Option<&Instruction<T>> {
29        self.0.values().find(|ref instr| instr.name == name)
30    }
31
32    /// Insert an instruction into the table.
33    pub fn insert(&mut self, instr: Instruction<T>) {
34        self.0.insert(instr.op_code, instr);
35    }
36
37    /// Returns `true` if the instruction table is empty.
38    pub fn is_empty(&self) -> bool {
39        self.0.is_empty()
40    }
41
42    /// Returns a list of symbols for use in the `Code` struct.
43    ///
44    /// Generates a vector of tuples containing the op code and the name of
45    /// each instruction.
46    pub fn symbols(&self) -> Vec<(usize, String)> {
47        let mut result = vec![];
48        self.0.keys().for_each(|ref key| {
49            let instr = &self.0[key];
50            result.push((instr.op_code, instr.name.clone()));
51        });
52        result.sort_by(|lhs, rhs| lhs.0.cmp(&rhs.0));
53        result
54    }
55}
56
57#[cfg(test)]
58mod test {
59    use super::*;
60    use crate::machine::Machine;
61
62    fn noop(_machine: &mut Machine<usize>, _args: &[usize]) {}
63
64    #[test]
65    fn new() {
66        let table: InstructionTable<usize> = InstructionTable::new();
67        assert!(table.is_empty())
68    }
69
70    #[test]
71    fn insert() {
72        let mut table: InstructionTable<usize> = InstructionTable::new();
73        assert!(table.is_empty());
74        table.insert(Instruction::new(0, "NOOP", 0, noop));
75        assert!(!table.is_empty());
76    }
77
78    #[test]
79    fn by_op_code() {
80        let mut table: InstructionTable<usize> = InstructionTable::new();
81        table.insert(Instruction::new(0, "NOOP", 0, noop));
82        let instr = table.by_op_code(0).unwrap();
83        assert_eq!(instr.name, "NOOP");
84    }
85
86    #[test]
87    fn by_name() {
88        let mut table: InstructionTable<usize> = InstructionTable::new();
89        table.insert(Instruction::new(0, "NOOP", 0, noop));
90        let instr = table.by_name("NOOP").unwrap();
91        assert_eq!(instr.op_code, 0);
92    }
93}