reqlang_expr/
disassembler.rs

1//! The dissassembler and associated types
2
3use crate::{
4    compiler::{CompileTimeEnv, ExprByteCode, opcode},
5    prelude::lookup,
6};
7
8pub struct Disassembler<'bytecode, 'env> {
9    bytecode: &'bytecode ExprByteCode,
10    env: &'env CompileTimeEnv,
11}
12
13impl<'bytecode, 'env> Disassembler<'bytecode, 'env> {
14    pub fn new(bytecode: &'bytecode ExprByteCode, env: &'env CompileTimeEnv) -> Self {
15        Self { bytecode, env }
16    }
17
18    /// Visualize the byte code as text
19    pub fn disassemble(&self) -> String {
20        let mut out = String::new();
21
22        let mut op_idx = 0;
23
24        while op_idx < self.bytecode.codes().len() {
25            let (op_byte_size, disassembled_byte_idx, disassembled_op) =
26                self.disassemble_op(op_idx);
27
28            let op_string = &format!("{disassembled_byte_idx} {disassembled_op}");
29            out.push_str(op_string);
30
31            op_idx += op_byte_size;
32        }
33
34        out
35    }
36
37    pub fn disassemble_op(&self, op_idx: usize) -> (usize, String, String) {
38        let op_idx_str = format!("{op_idx:04}");
39
40        let (op_idx_inc, op_str): (usize, String) = match self.bytecode.codes()[op_idx] {
41            opcode::GET => self.disassemble_op_get(op_idx),
42            opcode::CALL => self.disassemble_op_call("CALL", op_idx),
43            opcode::CONSTANT => self.disassemble_op_constant("CONSTANT", op_idx),
44            opcode::TRUE => self.disassemble_op_true("TRUE", op_idx),
45            opcode::FALSE => self.disassemble_op_false("FALSE", op_idx),
46            opcode::NOT => self.disassemble_op_u8("NOT", op_idx, opcode::NOT),
47            opcode::EQ => self.disassemble_op_u8("EQ", op_idx, opcode::EQ),
48            _ => (1, "".to_string()),
49        };
50
51        (op_idx_inc, op_idx_str, op_str)
52    }
53
54    fn disassemble_op_u8(&self, name: &str, op_idx: usize, expected: u8) -> (usize, String) {
55        let constant_op = self.bytecode.codes()[op_idx];
56        assert_eq!(constant_op, expected);
57
58        let string = format!("{name}\n");
59
60        (1, string)
61    }
62
63    fn disassemble_op_true(&self, name: &str, op_idx: usize) -> (usize, String) {
64        let constant_op = self.bytecode.codes()[op_idx];
65        assert_eq!(constant_op, opcode::TRUE);
66
67        let string = format!("{name}\n");
68
69        (1, string)
70    }
71
72    fn disassemble_op_false(&self, name: &str, op_idx: usize) -> (usize, String) {
73        let constant_op = self.bytecode.codes()[op_idx];
74        assert_eq!(constant_op, opcode::FALSE);
75
76        let string = format!("{name}\n");
77
78        (1, string)
79    }
80
81    fn disassemble_op_constant(&self, name: &str, op_idx: usize) -> (usize, String) {
82        let constant_op = self.bytecode.codes()[op_idx];
83        assert_eq!(constant_op, opcode::CONSTANT);
84
85        let constant_idx = self.bytecode.codes()[op_idx + 1] as usize;
86
87        let value = self
88            .bytecode
89            .strings()
90            .get(constant_idx)
91            .expect("should have string at index");
92
93        let string = format!("{name:16} {constant_idx:>4} == '{value}'\n");
94
95        (2, string)
96    }
97
98    fn disassemble_op_get(&self, op_idx: usize) -> (usize, String) {
99        let call_op = self.bytecode.codes()[op_idx];
100        assert_eq!(call_op, opcode::GET);
101
102        let lookup_type = self.bytecode.codes()[op_idx + 1];
103        let constant_idx = self.bytecode.codes()[op_idx + 2] as usize;
104
105        let value = match lookup_type {
106            lookup::BUILTIN => {
107                let value = self.env.get_builtin(constant_idx).unwrap();
108                &value.name
109            }
110            lookup::USER_BUILTIN => {
111                let value = self.env.get_user_builtin(constant_idx).unwrap();
112                &value.name
113            }
114            lookup::VAR => {
115                let value = self.env.get_var(constant_idx).unwrap();
116
117                value
118            }
119            lookup::PROMPT => {
120                let value = self.env.get_prompt(constant_idx).unwrap();
121
122                value
123            }
124            lookup::SECRET => {
125                let value = self.env.get_secret(constant_idx).unwrap();
126
127                value
128            }
129            lookup::CLIENT_CTX => {
130                let value = self.env.get_client_context(constant_idx).unwrap();
131
132                value
133            }
134            _ => panic!("invalid get lookup code: {}", lookup_type),
135        };
136
137        let lookup_type_string = match lookup_type {
138            lookup::BUILTIN => "BUILTIN",
139            lookup::USER_BUILTIN => "USER_BUILTIN",
140            lookup::VAR => "VAR",
141            lookup::PROMPT => "PROMPT",
142            lookup::SECRET => "SECRET",
143            lookup::CLIENT_CTX => "CLIENT_CTX",
144            _ => panic!("invalid get lookup code: {}", lookup_type),
145        };
146
147        let name = "GET";
148
149        let string = format!("{name} {lookup_type_string:12} {constant_idx:>4} == '{value}'\n");
150
151        (3, string)
152    }
153
154    fn disassemble_op_call(&self, name: &str, op_idx: usize) -> (usize, String) {
155        let call_op = self.bytecode.codes()[op_idx];
156        assert_eq!(call_op, opcode::CALL);
157
158        let arg_count = self.bytecode.codes()[op_idx + 1];
159
160        let string = format!("{name:16} ({arg_count} args)\n",);
161
162        (2, string)
163    }
164}