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 _ => (1, "".to_string()),
57 };
58
59 (op_idx_inc, op_idx_str, op_str)
60 }
61
62 fn disassemble_op_true(&self, name: &str, op_idx: usize) -> (usize, String) {
73 let constant_op = self.bytecode.codes()[op_idx];
74 assert_eq!(constant_op, opcode::TRUE);
75
76 let string = format!("{name}\n");
77
78 (1, string)
79 }
80
81 fn disassemble_op_false(&self, name: &str, op_idx: usize) -> (usize, String) {
82 let constant_op = self.bytecode.codes()[op_idx];
83 assert_eq!(constant_op, opcode::FALSE);
84
85 let string = format!("{name}\n");
86
87 (1, string)
88 }
89
90 fn disassemble_op_constant(&self, name: &str, op_idx: usize) -> (usize, String) {
91 let constant_op = self.bytecode.codes()[op_idx];
92 assert_eq!(constant_op, opcode::CONSTANT);
93
94 let constant_idx = self.bytecode.codes()[op_idx + 1] as usize;
95
96 let value = self
97 .bytecode
98 .strings()
99 .get(constant_idx)
100 .expect("should have string at index");
101
102 let string = format!("{name:16} {constant_idx:>4} == '{value}'\n");
103
104 (2, string)
105 }
106
107 fn disassemble_op_get(&self, op_idx: usize) -> (usize, String) {
108 let call_op = self.bytecode.codes()[op_idx];
109 assert_eq!(call_op, opcode::GET);
110
111 let lookup_type = self.bytecode.codes()[op_idx + 1];
112 let constant_idx = self.bytecode.codes()[op_idx + 2] as usize;
113
114 let value = match lookup_type {
115 lookup::BUILTIN => {
116 let value = self.env.get_builtin(constant_idx).unwrap();
117 &value.name
118 }
119 lookup::USER_BUILTIN => {
120 let value = self.env.get_user_builtin(constant_idx).unwrap();
121 &value.name
122 }
123 lookup::VAR => {
124 let value = self.env.get_var(constant_idx).unwrap();
125
126 value.as_str()
127 }
128 lookup::PROMPT => {
129 let value = self.env.get_prompt(constant_idx).unwrap();
130
131 value
132 }
133 lookup::SECRET => {
134 let value = self.env.get_secret(constant_idx).unwrap();
135
136 value
137 }
138 lookup::CLIENT_CTX => {
139 let value = self.env.get_client_context(constant_idx).unwrap();
140
141 value
142 }
143 lookup::TYPE => {
144 let value = self.bytecode.types().get(constant_idx).unwrap();
145
146 &value.name()
147 }
148 _ => panic!("invalid get lookup code: {}", lookup_type),
149 };
150
151 let lookup_type_string = match lookup_type {
152 lookup::BUILTIN => "BUILTIN",
153 lookup::USER_BUILTIN => "USER_BUILTIN",
154 lookup::VAR => "VAR",
155 lookup::PROMPT => "PROMPT",
156 lookup::SECRET => "SECRET",
157 lookup::CLIENT_CTX => "CLIENT_CTX",
158 lookup::TYPE => "TYPE",
159 _ => panic!("invalid get lookup code: {}", lookup_type),
160 };
161
162 let name = "GET";
163
164 let string = format!("{name} {lookup_type_string:12} {constant_idx:>4} == '{value}'\n");
165
166 (3, string)
167 }
168
169 fn disassemble_op_call(&self, name: &str, op_idx: usize) -> (usize, String) {
170 let call_op = self.bytecode.codes()[op_idx];
171 assert_eq!(call_op, opcode::CALL);
172
173 let arg_count = self.bytecode.codes()[op_idx + 1];
174
175 let string = format!("{name:16} ({arg_count} args)\n",);
176
177 (2, string)
178 }
179}