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 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}