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            _ => (1, "".to_string()),
47        };
48
49        (op_idx_inc, op_idx_str, op_str)
50    }
51
52    fn disassemble_op_true(&self, name: &str, op_idx: usize) -> (usize, String) {
53        let constant_op = self.bytecode.codes()[op_idx];
54        assert_eq!(constant_op, opcode::TRUE);
55
56        let string = format!("{name}\n");
57
58        (1, string)
59    }
60
61    fn disassemble_op_false(&self, name: &str, op_idx: usize) -> (usize, String) {
62        let constant_op = self.bytecode.codes()[op_idx];
63        assert_eq!(constant_op, opcode::FALSE);
64
65        let string = format!("{name}\n");
66
67        (1, string)
68    }
69
70    fn disassemble_op_constant(&self, name: &str, op_idx: usize) -> (usize, String) {
71        let constant_op = self.bytecode.codes()[op_idx];
72        assert_eq!(constant_op, opcode::CONSTANT);
73
74        let constant_idx = self.bytecode.codes()[op_idx + 1] as usize;
75
76        let value = self
77            .bytecode
78            .strings()
79            .get(constant_idx)
80            .expect("should have string at index");
81
82        let string = format!("{name:16} {constant_idx:>4} == '{value}'\n");
83
84        (2, string)
85    }
86
87    fn disassemble_op_get(&self, op_idx: usize) -> (usize, String) {
88        let call_op = self.bytecode.codes()[op_idx];
89        assert_eq!(call_op, opcode::GET);
90
91        let lookup_type = self.bytecode.codes()[op_idx + 1];
92        let constant_idx = self.bytecode.codes()[op_idx + 2] as usize;
93
94        let value = match lookup_type {
95            lookup::BUILTIN => {
96                let value = self.env.get_builtin(constant_idx).unwrap();
97                &value.name
98            }
99            lookup::USER_BUILTIN => {
100                let value = self.env.get_user_builtin(constant_idx).unwrap();
101                &value.name
102            }
103            lookup::VAR => {
104                let value = self.env.get_var(constant_idx).unwrap();
105
106                value
107            }
108            lookup::PROMPT => {
109                let value = self.env.get_prompt(constant_idx).unwrap();
110
111                value
112            }
113            lookup::SECRET => {
114                let value = self.env.get_secret(constant_idx).unwrap();
115
116                value
117            }
118            lookup::CLIENT_CTX => {
119                let value = self.env.get_client_context(constant_idx).unwrap();
120
121                value
122            }
123            _ => panic!("invalid get lookup code: {}", lookup_type),
124        };
125
126        let lookup_type_string = match lookup_type {
127            lookup::BUILTIN => "BUILTIN",
128            lookup::USER_BUILTIN => "USER_BUILTIN",
129            lookup::VAR => "VAR",
130            lookup::PROMPT => "PROMPT",
131            lookup::SECRET => "SECRET",
132            lookup::CLIENT_CTX => "CLIENT_CTX",
133            _ => panic!("invalid get lookup code: {}", lookup_type),
134        };
135
136        let name = "GET";
137
138        let string = format!("{name} {lookup_type_string:12} {constant_idx:>4} == '{value}'\n");
139
140        (3, string)
141    }
142
143    fn disassemble_op_call(&self, name: &str, op_idx: usize) -> (usize, String) {
144        let call_op = self.bytecode.codes()[op_idx];
145        assert_eq!(call_op, opcode::CALL);
146
147        let arg_count = self.bytecode.codes()[op_idx + 1];
148
149        let string = format!("{name:16} ({arg_count} args)\n",);
150
151        (2, string)
152    }
153}