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