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