Skip to main content

compiler/
op_code.rs

1use std::collections::HashMap;
2
3use byteorder;
4use byteorder::{BigEndian, ByteOrder, WriteBytesExt};
5
6use strum::{EnumCount, EnumIter, FromRepr};
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
19impl OpcodeDefinition {
20    pub fn name(&self) -> &'static str {
21        self.name
22    }
23
24    pub fn operand_widths(&self) -> &[i32] {
25        &self.operand_width
26    }
27}
28
29#[repr(u8)]
30#[derive(Debug, Hash, Eq, Clone, Copy, PartialEq, EnumCount, EnumIter, FromRepr)]
31pub enum Opcode {
32    OpConst,
33    OpAdd,
34    OpPop,
35    OpSub,
36    OpMul,
37    OpDiv,
38    OpTrue,
39    OpFalse,
40    OpEqual,
41    OpNotEqual,
42    OpGreaterThan,
43    OpMinus,
44    OpBang,
45    OpJumpNotTruthy,
46    OpJump,
47    OpNull,
48    OpGetGlobal,
49    OpSetGlobal,
50    OpArray,
51    OpHash,
52    OpIndex,
53    OpCall,
54    OpReturnValue,
55    OpReturn,
56    OpGetLocal,
57    OpSetLocal,
58    OpGetBuiltin,
59    OpClosure,
60    OpGetFree,
61    OpCurrentClosure,
62    OpClass,
63    OpMethod,
64    OpGetProperty,
65    OpSetProperty,
66    OpNew,
67    // Appended to preserve the byte values of all existing opcodes.
68    OpLessThan,
69}
70
71lazy_static! {
72    pub static ref DEFINITIONS: HashMap<Opcode, OpcodeDefinition> = {
73        let mut m = HashMap::new();
74        m.insert(
75            Opcode::OpConst,
76            OpcodeDefinition {
77                name: "OpConst",
78                operand_width: vec![2],
79            },
80        );
81        m.insert(
82            Opcode::OpAdd,
83            OpcodeDefinition {
84                name: "OpAdd",
85                operand_width: vec![],
86            },
87        );
88        m.insert(
89            Opcode::OpPop,
90            OpcodeDefinition {
91                name: "OpPop",
92                operand_width: vec![],
93            },
94        );
95        m.insert(
96            Opcode::OpSub,
97            OpcodeDefinition {
98                name: "OpSub",
99                operand_width: vec![],
100            },
101        );
102        m.insert(
103            Opcode::OpMul,
104            OpcodeDefinition {
105                name: "OpMul",
106                operand_width: vec![],
107            },
108        );
109        m.insert(
110            Opcode::OpDiv,
111            OpcodeDefinition {
112                name: "OpDiv",
113                operand_width: vec![],
114            },
115        );
116        m.insert(
117            Opcode::OpTrue,
118            OpcodeDefinition {
119                name: "OpTrue",
120                operand_width: vec![],
121            },
122        );
123        m.insert(
124            Opcode::OpFalse,
125            OpcodeDefinition {
126                name: "OpFalse",
127                operand_width: vec![],
128            },
129        );
130        m.insert(
131            Opcode::OpEqual,
132            OpcodeDefinition {
133                name: "OpEqual",
134                operand_width: vec![],
135            },
136        );
137        m.insert(
138            Opcode::OpNotEqual,
139            OpcodeDefinition {
140                name: "OpNotEqual",
141                operand_width: vec![],
142            },
143        );
144        m.insert(
145            Opcode::OpGreaterThan,
146            OpcodeDefinition {
147                name: "OpGreatThan",
148                operand_width: vec![],
149            },
150        );
151        m.insert(
152            Opcode::OpLessThan,
153            OpcodeDefinition {
154                name: "OpLessThan",
155                operand_width: vec![],
156            },
157        );
158        m.insert(
159            Opcode::OpMinus,
160            OpcodeDefinition {
161                name: "OpMinus",
162                operand_width: vec![],
163            },
164        );
165        m.insert(
166            Opcode::OpBang,
167            OpcodeDefinition {
168                name: "OpBang",
169                operand_width: vec![],
170            },
171        );
172        m.insert(
173            Opcode::OpJumpNotTruthy,
174            OpcodeDefinition {
175                name: "OpJumpNotTruthy",
176                operand_width: vec![2],
177            },
178        );
179        m.insert(
180            Opcode::OpJump,
181            OpcodeDefinition {
182                name: "OpJump",
183                operand_width: vec![2],
184            },
185        );
186        m.insert(
187            Opcode::OpNull,
188            OpcodeDefinition {
189                name: "OpNull",
190                operand_width: vec![],
191            },
192        );
193        m.insert(
194            Opcode::OpGetGlobal,
195            OpcodeDefinition {
196                name: "OpGetGlobal",
197                operand_width: vec![2],
198            },
199        );
200        m.insert(
201            Opcode::OpSetGlobal,
202            OpcodeDefinition {
203                name: "OpSetGlobal",
204                operand_width: vec![2],
205            },
206        );
207        m.insert(
208            Opcode::OpArray,
209            OpcodeDefinition {
210                name: "OpArray",
211                operand_width: vec![2],
212            },
213        );
214        m.insert(
215            Opcode::OpHash,
216            OpcodeDefinition {
217                name: "OpHash",
218                operand_width: vec![2],
219            },
220        );
221        m.insert(
222            Opcode::OpIndex,
223            OpcodeDefinition {
224                name: "OpIndex",
225                operand_width: vec![],
226            },
227        );
228        m.insert(
229            Opcode::OpCall,
230            OpcodeDefinition {
231                name: "OpCall",
232                operand_width: vec![1],
233            },
234        );
235        m.insert(
236            Opcode::OpReturn,
237            OpcodeDefinition {
238                name: "OpReturn",
239                operand_width: vec![],
240            },
241        );
242        m.insert(
243            Opcode::OpReturnValue,
244            OpcodeDefinition {
245                name: "OpReturnValue",
246                operand_width: vec![],
247            },
248        );
249        m.insert(
250            Opcode::OpGetLocal,
251            OpcodeDefinition {
252                name: "OpGetLocal",
253                operand_width: vec![1],
254            },
255        );
256        m.insert(
257            Opcode::OpSetLocal,
258            OpcodeDefinition {
259                name: "OpSetLocal",
260                operand_width: vec![1],
261            },
262        );
263        m.insert(
264            Opcode::OpGetBuiltin,
265            OpcodeDefinition {
266                name: "OpGetBuiltin",
267                operand_width: vec![1],
268            },
269        );
270        m.insert(
271            Opcode::OpClosure,
272            OpcodeDefinition {
273                name: "OpClosure",
274                operand_width: vec![2, 1],
275            },
276        );
277        m.insert(
278            Opcode::OpGetFree,
279            OpcodeDefinition {
280                name: "OpGetFree",
281                operand_width: vec![1],
282            },
283        );
284        m.insert(
285            Opcode::OpCurrentClosure,
286            OpcodeDefinition {
287                name: "OpCurrentClosure",
288                operand_width: vec![],
289            },
290        );
291        m.insert(
292            Opcode::OpClass,
293            OpcodeDefinition {
294                name: "OpClass",
295                operand_width: vec![2],
296            },
297        );
298        m.insert(
299            Opcode::OpMethod,
300            OpcodeDefinition {
301                name: "OpMethod",
302                operand_width: vec![2, 1],
303            },
304        );
305        m.insert(
306            Opcode::OpGetProperty,
307            OpcodeDefinition {
308                name: "OpGetProperty",
309                operand_width: vec![2],
310            },
311        );
312        m.insert(
313            Opcode::OpSetProperty,
314            OpcodeDefinition {
315                name: "OpSetProperty",
316                operand_width: vec![2],
317            },
318        );
319        m.insert(
320            Opcode::OpNew,
321            OpcodeDefinition {
322                name: "OpNew",
323                operand_width: vec![1],
324            },
325        );
326        return m;
327    };
328}
329
330pub fn make_instructions(op: Opcode, operands: &[usize]) -> Instructions {
331    let mut instructions = Vec::new();
332    instructions.push(op as u8);
333    let widths = &DEFINITIONS.get(&op).unwrap().operand_width;
334
335    for (o, w) in operands.iter().zip(widths) {
336        match w {
337            2 => {
338                instructions.write_u16::<BigEndian>(*o as u16).unwrap();
339            }
340            1 => {
341                instructions.write_u8(*o as u8).unwrap();
342            }
343            _ => {
344                panic!("unsupported operand width {}", w)
345            }
346        }
347    }
348
349    return Instructions {
350        data: instructions,
351    };
352}
353
354pub fn read_operands(def: &OpcodeDefinition, ins: &[u8]) -> (Vec<usize>, usize) {
355    let mut operands = Vec::with_capacity(def.operand_width.len());
356    let mut offset = 0;
357
358    for w in &def.operand_width {
359        match w {
360            2 => {
361                operands.push(BigEndian::read_u16(&ins[offset..offset + 2]) as usize);
362                offset += 2;
363            }
364            1 => {
365                operands.push(ins[offset] as usize);
366                offset += 1;
367            }
368            0 => {}
369            _ => {
370                panic!("unsupported operand width {} for read", w)
371            }
372        }
373    }
374
375    return (operands, offset);
376}
377
378pub fn concat_instructions(expected: &Vec<Instructions>) -> Instructions {
379    let mut out = Instructions {
380        data: vec![],
381    };
382
383    for instruction in expected {
384        out = out.merge_instructions(instruction)
385    }
386
387    return out;
388}
389
390impl Instructions {
391    // prettify bytecodes
392    pub fn string(&self) -> String {
393        let mut ret = String::new();
394        let mut i = 0;
395        while i < self.data.len() {
396            let op: u8 = self.data[i];
397            let Some(opcode) = Opcode::from_repr(op) else {
398                ret.push_str(&format!("{:04} <unknown opcode 0x{:02x}>\n", i, op));
399                i += 1;
400                continue;
401            };
402
403            let definition = DEFINITIONS.get(&opcode).unwrap();
404            let width: usize = definition.operand_width.iter().map(|w| *w as usize).sum();
405            if i + 1 + width > self.data.len() {
406                ret.push_str(&format!("{:04} {} <truncated operands>\n", i, definition.name));
407                break;
408            }
409            let (operands, read_size) = read_operands(definition, &self.data[i + 1..]);
410            ret.push_str(&format!("{:04} {}\n", i, Self::fmt_instructions(definition, &operands)));
411            i = i + 1 + read_size;
412        }
413
414        return ret;
415    }
416
417    fn fmt_instructions(def: &OpcodeDefinition, operands: &[usize]) -> String {
418        match def.operand_width.len() {
419            2 => format!("{} {} {}", def.name, operands[0], operands[1]),
420            1 => format!("{} {}", def.name, operands[0]),
421            0 => def.name.to_string(),
422            _ => {
423                panic!("unsupported operand width {}", def.operand_width.len());
424            }
425        }
426    }
427
428    pub fn merge_instructions(&self, other: &Instructions) -> Instructions {
429        let ins = [self, other];
430        // Maybe extend_from_slice, but I have not make it work
431        // https://stackoverflow.com/a/69578632/1713757
432        return Instructions {
433            data: ins
434                .iter()
435                .fold(vec![], |sum, &i| [sum.as_slice(), i.data.as_slice()].concat()),
436        };
437    }
438}