compiler/
op_code.rs

1use std::collections::HashMap;
2
3use byteorder;
4use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
5
6use strum::{EnumCount, EnumIter};
7
8// why not type, see https://stackoverflow.com/a/35569079/1713757
9#[derive(Hash, Eq, Debug, Clone, PartialEq, PartialOrd)]
10pub struct Instructions {
11    pub data: Vec<u8>,
12}
13
14pub struct OpcodeDefinition {
15    pub(crate) name: &'static str,
16    operand_width: Vec<i32>,
17}
18
19#[repr(u8)]
20#[derive(Debug, Hash, Eq, Clone, Copy, PartialEq, EnumCount, EnumIter)]
21pub enum Opcode {
22    OpConst,
23    OpAdd,
24    OpPop,
25    OpSub,
26    OpMul,
27    OpDiv,
28    OpTrue,
29    OpFalse,
30    OpEqual,
31    OpNotEqual,
32    OpGreaterThan,
33    OpMinus,
34    OpBang,
35    OpJumpNotTruthy,
36    OpJump,
37    OpNull,
38    OpGetGlobal,
39    OpSetGlobal,
40    OpArray,
41    OpHash,
42    OpIndex,
43    OpCall,
44    OpReturnValue,
45    OpReturn,
46    OpGetLocal,
47    OpSetLocal,
48    OpGetBuiltin,
49    OpClosure,
50    OpGetFree,
51    OpCurrentClosure,
52}
53
54lazy_static! {
55    pub static ref DEFINITIONS: HashMap<Opcode, OpcodeDefinition> = {
56        let mut m = HashMap::new();
57        m.insert(Opcode::OpConst, OpcodeDefinition { name: "OpConst", operand_width: vec![2] });
58        m.insert(Opcode::OpAdd, OpcodeDefinition { name: "OpAdd", operand_width: vec![] });
59        m.insert(Opcode::OpPop, OpcodeDefinition { name: "OpPop", operand_width: vec![] });
60        m.insert(Opcode::OpSub, OpcodeDefinition { name: "OpSub", operand_width: vec![] });
61        m.insert(Opcode::OpMul, OpcodeDefinition { name: "OpMul", operand_width: vec![] });
62        m.insert(Opcode::OpDiv, OpcodeDefinition { name: "OpDiv", operand_width: vec![] });
63        m.insert(Opcode::OpTrue, OpcodeDefinition { name: "OpTrue", operand_width: vec![] });
64        m.insert(Opcode::OpFalse, OpcodeDefinition { name: "OpFalse", operand_width: vec![] });
65        m.insert(Opcode::OpEqual, OpcodeDefinition { name: "OpEqual", operand_width: vec![] });
66        m.insert(
67            Opcode::OpNotEqual,
68            OpcodeDefinition { name: "OpNotEqual", operand_width: vec![] },
69        );
70        m.insert(
71            Opcode::OpGreaterThan,
72            OpcodeDefinition { name: "OpGreatThan", operand_width: vec![] },
73        );
74        m.insert(Opcode::OpMinus, OpcodeDefinition { name: "OpMinus", operand_width: vec![] });
75        m.insert(Opcode::OpBang, OpcodeDefinition { name: "OpBang", operand_width: vec![] });
76        m.insert(
77            Opcode::OpJumpNotTruthy,
78            OpcodeDefinition { name: "OpJumpNotTruthy", operand_width: vec![2] },
79        );
80        m.insert(Opcode::OpJump, OpcodeDefinition { name: "OpJump", operand_width: vec![2] });
81        m.insert(Opcode::OpNull, OpcodeDefinition { name: "OpNull", operand_width: vec![] });
82        m.insert(
83            Opcode::OpGetGlobal,
84            OpcodeDefinition { name: "OpGetGlobal", operand_width: vec![2] },
85        );
86        m.insert(
87            Opcode::OpSetGlobal,
88            OpcodeDefinition { name: "OpSetGlobal", operand_width: vec![2] },
89        );
90        m.insert(Opcode::OpArray, OpcodeDefinition { name: "OpArray", operand_width: vec![2] });
91        m.insert(Opcode::OpHash, OpcodeDefinition { name: "OpHash", operand_width: vec![2] });
92        m.insert(Opcode::OpIndex, OpcodeDefinition { name: "OpIndex", operand_width: vec![] });
93        m.insert(Opcode::OpCall, OpcodeDefinition { name: "OpCall", operand_width: vec![1] });
94        m.insert(Opcode::OpReturn, OpcodeDefinition { name: "OpReturn", operand_width: vec![] });
95        m.insert(
96            Opcode::OpReturnValue,
97            OpcodeDefinition { name: "OpReturnValue", operand_width: vec![] },
98        );
99        m.insert(
100            Opcode::OpGetLocal,
101            OpcodeDefinition { name: "OpGetLocal", operand_width: vec![1] },
102        );
103        m.insert(
104            Opcode::OpSetLocal,
105            OpcodeDefinition { name: "OpSetLocal", operand_width: vec![1] },
106        );
107        m.insert(
108            Opcode::OpGetBuiltin,
109            OpcodeDefinition { name: "OpGetBuiltin", operand_width: vec![1] },
110        );
111        m.insert(
112            Opcode::OpClosure,
113            OpcodeDefinition { name: "OpClosure", operand_width: vec![2, 1] },
114        );
115        m.insert(
116            Opcode::OpGetFree,
117            OpcodeDefinition { name: "OpGetFree", operand_width: vec![1] },
118        );
119        m.insert(
120            Opcode::OpCurrentClosure,
121            OpcodeDefinition { name: "OpCurrentClosure", operand_width: vec![] },
122        );
123        return m;
124    };
125}
126
127pub fn make_instructions(op: Opcode, operands: &Vec<usize>) -> Instructions {
128    let mut instructions = Vec::new();
129    instructions.push(op as u8);
130    let widths = &DEFINITIONS.get(&op).unwrap().operand_width;
131
132    for (o, w) in operands.into_iter().zip(widths) {
133        match w {
134            2 => {
135                instructions.write_u16::<BigEndian>(*o as u16).unwrap();
136            }
137            1 => {
138                instructions.write_u8(*o as u8).unwrap();
139            }
140            _ => {
141                panic!("unsupported operand width {}", w)
142            }
143        }
144    }
145
146    return Instructions { data: instructions };
147}
148
149pub fn read_operands(def: &OpcodeDefinition, ins: &[u8]) -> (Vec<usize>, usize) {
150    let mut operands = Vec::with_capacity(def.operand_width.len());
151    let mut offset = 0;
152
153    for w in &def.operand_width {
154        match w {
155            2 => {
156                operands.push(BigEndian::read_u16(&ins[offset..offset + 2]) as usize);
157                offset = offset + 2;
158            }
159            1 => {
160                operands.push(ins[offset] as usize);
161                offset = offset + 1;
162            }
163            0 => {}
164            _ => {
165                panic!("unsupported operand width {} for read", w)
166            }
167        }
168    }
169
170    return (operands, offset);
171}
172
173pub fn concat_instructions(expected: &Vec<Instructions>) -> Instructions {
174    let mut out = Instructions { data: vec![] };
175
176    for instruction in expected {
177        out = out.merge_instructions(instruction)
178    }
179
180    return out;
181}
182
183pub fn cast_u8_to_opcode(op: u8) -> Opcode {
184    // https://stackoverflow.com/a/42382144/1713757
185    return unsafe { ::std::mem::transmute(op) };
186}
187
188impl Instructions {
189    // prettify bytecodes
190    pub fn string(&self) -> String {
191        let mut ret = String::new();
192        let mut i = 0;
193        while i < self.data.len() {
194            let op: u8 = *self.data.get(i).unwrap();
195            let opcode = cast_u8_to_opcode(op);
196
197            let definition = DEFINITIONS.get(&opcode).unwrap();
198            let (operands, read_size) = read_operands(definition, &self.data[i + 1..]);
199            ret.push_str(&format!("{:04} {}\n", i, Self::fmt_instructions(definition, &operands)));
200            i = i + 1 + read_size;
201        }
202
203        return ret;
204    }
205
206    fn fmt_instructions(def: &OpcodeDefinition, operands: &Vec<usize>) -> String {
207        match def.operand_width.len() {
208            2 => format!("{} {} {}", def.name, operands[0], operands[1]),
209            1 => format!("{} {}", def.name, operands[0]),
210            0 => format!("{}", def.name),
211            _ => {
212                panic!("unsupported operand width {}", def.operand_width.len());
213            }
214        }
215    }
216
217    pub fn merge_instructions(&self, other: &Instructions) -> Instructions {
218        let ins = vec![self, other];
219        // Maybe extend_from_slice, but I have not make it work
220        // https://stackoverflow.com/a/69578632/1713757
221        return Instructions {
222            data: ins
223                .iter()
224                .fold(vec![], |sum, &i| [sum.as_slice(), i.data.as_slice()].concat()),
225        };
226    }
227}