/Users/alexjokela/projects/lattice/src/chunk.c
Line | Count | Source |
1 | | #include "chunk.h" |
2 | | #include "stackopcode.h" |
3 | | #include <stdlib.h> |
4 | | #include <stdio.h> |
5 | | #include <string.h> |
6 | | |
7 | 30.8k | static size_t chunk_fnv1a(const char *key) { |
8 | 30.8k | size_t hash = 14695981039346656037ULL; |
9 | 445k | for (const char *p = key; *p; p++) { |
10 | 414k | hash ^= (size_t)(unsigned char)*p; |
11 | 414k | hash *= 1099511628211ULL; |
12 | 414k | } |
13 | 30.8k | return hash; |
14 | 30.8k | } |
15 | | |
16 | 4.34k | Chunk *chunk_new(void) { |
17 | 4.34k | Chunk *c = calloc(1, sizeof(Chunk)); |
18 | 4.34k | c->code_cap = 256; |
19 | 4.34k | c->code = malloc(c->code_cap); |
20 | 4.34k | c->const_cap = 32; |
21 | 4.34k | c->constants = malloc(c->const_cap * sizeof(LatValue)); |
22 | 4.34k | c->const_hashes = calloc(c->const_cap, sizeof(size_t)); |
23 | 4.34k | c->lines_cap = 256; |
24 | 4.34k | c->lines = malloc(c->lines_cap * sizeof(int)); |
25 | 4.34k | return c; |
26 | 4.34k | } |
27 | | |
28 | 4.34k | void chunk_free(Chunk *c) { |
29 | 4.34k | if (!c) return; |
30 | 4.34k | free(c->code); |
31 | 38.8k | for (size_t i = 0; i < c->const_len; i++) { |
32 | | /* Recursively free compiled sub-chunks stored as VAL_CLOSURE constants */ |
33 | 34.4k | if (c->constants[i].type == VAL_CLOSURE && |
34 | 34.4k | c->constants[i].as.closure.body == NULL && |
35 | 34.4k | c->constants[i].as.closure.native_fn != NULL) { |
36 | 3.36k | chunk_free((Chunk *)c->constants[i].as.closure.native_fn); |
37 | 3.36k | c->constants[i].as.closure.native_fn = NULL; |
38 | 3.36k | } |
39 | 34.4k | value_free(&c->constants[i]); |
40 | 34.4k | } |
41 | 4.34k | free(c->constants); |
42 | 4.34k | free(c->const_hashes); |
43 | 4.34k | free(c->lines); |
44 | 29.5k | for (size_t i = 0; i < c->local_name_cap; i++) |
45 | 25.1k | free(c->local_names[i]); |
46 | 4.34k | free(c->local_names); |
47 | 4.34k | free(c->name); |
48 | 4.34k | if (c->default_values) { |
49 | 76 | for (int i = 0; i < c->default_count; i++) |
50 | 39 | value_free(&c->default_values[i]); |
51 | 37 | free(c->default_values); |
52 | 37 | } |
53 | 4.34k | free(c->param_phases); |
54 | 4.34k | if (c->export_names) { |
55 | 18 | for (size_t i = 0; i < c->export_count; i++) |
56 | 12 | free(c->export_names[i]); |
57 | 6 | free(c->export_names); |
58 | 6 | } |
59 | 4.34k | free(c); |
60 | 4.34k | } |
61 | | |
62 | 8.92k | void chunk_set_local_name(Chunk *c, size_t slot, const char *name) { |
63 | 12.0k | while (c->local_name_cap <= slot) { |
64 | 3.11k | size_t new_cap = c->local_name_cap ? c->local_name_cap * 2 : 8; |
65 | 3.11k | c->local_names = realloc(c->local_names, new_cap * sizeof(char *)); |
66 | 28.3k | for (size_t i = c->local_name_cap; i < new_cap; i++) |
67 | 25.1k | c->local_names[i] = NULL; |
68 | 3.11k | c->local_name_cap = new_cap; |
69 | 3.11k | } |
70 | 8.92k | free(c->local_names[slot]); |
71 | 8.92k | c->local_names[slot] = strdup(name); |
72 | 8.92k | } |
73 | | |
74 | 221k | size_t chunk_write(Chunk *c, uint8_t byte, int line) { |
75 | 221k | if (c->code_len >= c->code_cap) { |
76 | 90 | c->code_cap *= 2; |
77 | 90 | c->code = realloc(c->code, c->code_cap); |
78 | 90 | } |
79 | 221k | if (c->lines_len >= c->lines_cap) { |
80 | 90 | c->lines_cap *= 2; |
81 | 90 | c->lines = realloc(c->lines, c->lines_cap * sizeof(int)); |
82 | 90 | } |
83 | 221k | size_t offset = c->code_len; |
84 | 221k | c->code[c->code_len++] = byte; |
85 | 221k | c->lines[c->lines_len++] = line; |
86 | 221k | return offset; |
87 | 221k | } |
88 | | |
89 | 34.4k | size_t chunk_add_constant(Chunk *c, LatValue val) { |
90 | 34.4k | if (c->const_len >= c->const_cap) { |
91 | 168 | c->const_cap *= 2; |
92 | 168 | c->constants = realloc(c->constants, c->const_cap * sizeof(LatValue)); |
93 | 168 | c->const_hashes = realloc(c->const_hashes, c->const_cap * sizeof(size_t)); |
94 | 168 | } |
95 | 34.4k | size_t idx = c->const_len++; |
96 | 34.4k | c->constants[idx] = val; |
97 | 34.4k | c->const_hashes[idx] = (val.type == VAL_STR && val.as.str_val) ? |
98 | 30.8k | chunk_fnv1a(val.as.str_val) : 0; |
99 | 34.4k | return idx; |
100 | 34.4k | } |
101 | | |
102 | 0 | void chunk_write_u16(Chunk *c, uint16_t val, int line) { |
103 | 0 | chunk_write(c, (uint8_t)((val >> 8) & 0xff), line); |
104 | 0 | chunk_write(c, (uint8_t)(val & 0xff), line); |
105 | 0 | } |
106 | | |
107 | 0 | static size_t simple_instruction(const char *name, size_t offset) { |
108 | 0 | fprintf(stderr, "%s\n", name); |
109 | 0 | return offset + 1; |
110 | 0 | } |
111 | | |
112 | 0 | static size_t byte_instruction(const char *name, const Chunk *c, size_t offset) { |
113 | 0 | uint8_t slot = c->code[offset + 1]; |
114 | 0 | fprintf(stderr, "%-20s %4d\n", name, slot); |
115 | 0 | return offset + 2; |
116 | 0 | } |
117 | | |
118 | 0 | static size_t constant_instruction(const char *name, const Chunk *c, size_t offset) { |
119 | 0 | uint8_t idx = c->code[offset + 1]; |
120 | 0 | fprintf(stderr, "%-20s %4d '", name, idx); |
121 | 0 | if (idx < c->const_len) { |
122 | 0 | char *repr = value_repr(&c->constants[idx]); |
123 | 0 | fprintf(stderr, "%s", repr); |
124 | 0 | free(repr); |
125 | 0 | } |
126 | 0 | fprintf(stderr, "'\n"); |
127 | 0 | return offset + 2; |
128 | 0 | } |
129 | | |
130 | 0 | static size_t constant_instruction_16(const char *name, const Chunk *c, size_t offset) { |
131 | 0 | uint16_t idx = (uint16_t)(c->code[offset + 1] << 8) | c->code[offset + 2]; |
132 | 0 | fprintf(stderr, "%-20s %4d '", name, idx); |
133 | 0 | if (idx < c->const_len) { |
134 | 0 | char *repr = value_repr(&c->constants[idx]); |
135 | 0 | fprintf(stderr, "%s", repr); |
136 | 0 | free(repr); |
137 | 0 | } |
138 | 0 | fprintf(stderr, "'\n"); |
139 | 0 | return offset + 3; |
140 | 0 | } |
141 | | |
142 | 0 | static size_t jump_instruction(const char *name, int sign, const Chunk *c, size_t offset) { |
143 | 0 | uint16_t jump = (uint16_t)(c->code[offset + 1] << 8) | c->code[offset + 2]; |
144 | 0 | fprintf(stderr, "%-20s %4zu -> %zu\n", name, offset, offset + 3 + sign * jump); |
145 | 0 | return offset + 3; |
146 | 0 | } |
147 | | |
148 | 0 | static size_t closure_instruction(const Chunk *c, size_t offset) { |
149 | 0 | uint8_t fn_idx = c->code[offset + 1]; |
150 | 0 | uint8_t upvalue_count = c->code[offset + 2]; |
151 | 0 | fprintf(stderr, "%-20s %4d (upvalues: %d)\n", "OP_CLOSURE", fn_idx, upvalue_count); |
152 | 0 | size_t pos = offset + 3; |
153 | 0 | for (uint8_t i = 0; i < upvalue_count; i++) { |
154 | 0 | uint8_t is_local = c->code[pos++]; |
155 | 0 | uint8_t index = c->code[pos++]; |
156 | 0 | fprintf(stderr, " | %s %d\n", |
157 | 0 | is_local ? "local" : "upvalue", index); |
158 | 0 | } |
159 | 0 | return pos; |
160 | 0 | } |
161 | | |
162 | 0 | static size_t invoke_instruction(const Chunk *c, size_t offset) { |
163 | 0 | uint8_t method_idx = c->code[offset + 1]; |
164 | 0 | uint8_t arg_count = c->code[offset + 2]; |
165 | 0 | fprintf(stderr, "%-20s %4d '", "OP_INVOKE", method_idx); |
166 | 0 | if (method_idx < c->const_len) { |
167 | 0 | char *repr = value_repr(&c->constants[method_idx]); |
168 | 0 | fprintf(stderr, "%s", repr); |
169 | 0 | free(repr); |
170 | 0 | } |
171 | 0 | fprintf(stderr, "' (%d args)\n", arg_count); |
172 | 0 | return offset + 3; |
173 | 0 | } |
174 | | |
175 | 0 | static size_t build_struct_instruction(const Chunk *c, size_t offset) { |
176 | 0 | uint8_t name_idx = c->code[offset + 1]; |
177 | 0 | uint8_t field_count = c->code[offset + 2]; |
178 | 0 | fprintf(stderr, "%-20s %4d '", "OP_BUILD_STRUCT", name_idx); |
179 | 0 | if (name_idx < c->const_len) { |
180 | 0 | char *repr = value_repr(&c->constants[name_idx]); |
181 | 0 | fprintf(stderr, "%s", repr); |
182 | 0 | free(repr); |
183 | 0 | } |
184 | 0 | fprintf(stderr, "' (%d fields)\n", field_count); |
185 | 0 | return offset + 3; |
186 | 0 | } |
187 | | |
188 | 0 | static size_t build_enum_instruction(const Chunk *c, size_t offset) { |
189 | 0 | uint8_t enum_idx = c->code[offset + 1]; |
190 | 0 | uint8_t var_idx = c->code[offset + 2]; |
191 | 0 | uint8_t payload_count = c->code[offset + 3]; |
192 | 0 | fprintf(stderr, "%-20s enum=%d var=%d payload=%d\n", |
193 | 0 | "OP_BUILD_ENUM", enum_idx, var_idx, payload_count); |
194 | 0 | return offset + 4; |
195 | 0 | } |
196 | | |
197 | 0 | size_t chunk_disassemble_instruction(const Chunk *c, size_t offset) { |
198 | 0 | fprintf(stderr, "%04zu ", offset); |
199 | 0 | if (offset > 0 && c->lines[offset] == c->lines[offset - 1]) |
200 | 0 | fprintf(stderr, " | "); |
201 | 0 | else |
202 | 0 | fprintf(stderr, "%4d ", c->lines[offset]); |
203 | |
|
204 | 0 | uint8_t op = c->code[offset]; |
205 | 0 | switch (op) { |
206 | 0 | case OP_CONSTANT: return constant_instruction("OP_CONSTANT", c, offset); |
207 | 0 | case OP_NIL: return simple_instruction("OP_NIL", offset); |
208 | 0 | case OP_TRUE: return simple_instruction("OP_TRUE", offset); |
209 | 0 | case OP_FALSE: return simple_instruction("OP_FALSE", offset); |
210 | 0 | case OP_UNIT: return simple_instruction("OP_UNIT", offset); |
211 | 0 | case OP_POP: return simple_instruction("OP_POP", offset); |
212 | 0 | case OP_DUP: return simple_instruction("OP_DUP", offset); |
213 | 0 | case OP_SWAP: return simple_instruction("OP_SWAP", offset); |
214 | 0 | case OP_ADD: return simple_instruction("OP_ADD", offset); |
215 | 0 | case OP_SUB: return simple_instruction("OP_SUB", offset); |
216 | 0 | case OP_MUL: return simple_instruction("OP_MUL", offset); |
217 | 0 | case OP_DIV: return simple_instruction("OP_DIV", offset); |
218 | 0 | case OP_MOD: return simple_instruction("OP_MOD", offset); |
219 | 0 | case OP_NEG: return simple_instruction("OP_NEG", offset); |
220 | 0 | case OP_NOT: return simple_instruction("OP_NOT", offset); |
221 | 0 | case OP_BIT_AND: return simple_instruction("OP_BIT_AND", offset); |
222 | 0 | case OP_BIT_OR: return simple_instruction("OP_BIT_OR", offset); |
223 | 0 | case OP_BIT_XOR: return simple_instruction("OP_BIT_XOR", offset); |
224 | 0 | case OP_BIT_NOT: return simple_instruction("OP_BIT_NOT", offset); |
225 | 0 | case OP_LSHIFT: return simple_instruction("OP_LSHIFT", offset); |
226 | 0 | case OP_RSHIFT: return simple_instruction("OP_RSHIFT", offset); |
227 | 0 | case OP_EQ: return simple_instruction("OP_EQ", offset); |
228 | 0 | case OP_NEQ: return simple_instruction("OP_NEQ", offset); |
229 | 0 | case OP_LT: return simple_instruction("OP_LT", offset); |
230 | 0 | case OP_GT: return simple_instruction("OP_GT", offset); |
231 | 0 | case OP_LTEQ: return simple_instruction("OP_LTEQ", offset); |
232 | 0 | case OP_GTEQ: return simple_instruction("OP_GTEQ", offset); |
233 | 0 | case OP_CONCAT: return simple_instruction("OP_CONCAT", offset); |
234 | 0 | case OP_GET_LOCAL: return byte_instruction("OP_GET_LOCAL", c, offset); |
235 | 0 | case OP_SET_LOCAL: return byte_instruction("OP_SET_LOCAL", c, offset); |
236 | 0 | case OP_GET_GLOBAL: return constant_instruction("OP_GET_GLOBAL", c, offset); |
237 | 0 | case OP_SET_GLOBAL: return constant_instruction("OP_SET_GLOBAL", c, offset); |
238 | 0 | case OP_DEFINE_GLOBAL: return constant_instruction("OP_DEFINE_GLOBAL", c, offset); |
239 | 0 | case OP_GET_UPVALUE: return byte_instruction("OP_GET_UPVALUE", c, offset); |
240 | 0 | case OP_SET_UPVALUE: return byte_instruction("OP_SET_UPVALUE", c, offset); |
241 | 0 | case OP_CLOSE_UPVALUE: return simple_instruction("OP_CLOSE_UPVALUE", offset); |
242 | 0 | case OP_JUMP: return jump_instruction("OP_JUMP", 1, c, offset); |
243 | 0 | case OP_JUMP_IF_FALSE: return jump_instruction("OP_JUMP_IF_FALSE", 1, c, offset); |
244 | 0 | case OP_JUMP_IF_TRUE: return jump_instruction("OP_JUMP_IF_TRUE", 1, c, offset); |
245 | 0 | case OP_JUMP_IF_NOT_NIL: return jump_instruction("OP_JUMP_IF_NOT_NIL", 1, c, offset); |
246 | 0 | case OP_LOOP: return jump_instruction("OP_LOOP", -1, c, offset); |
247 | 0 | case OP_CALL: return byte_instruction("OP_CALL", c, offset); |
248 | 0 | case OP_CLOSURE: return closure_instruction(c, offset); |
249 | 0 | case OP_RETURN: return simple_instruction("OP_RETURN", offset); |
250 | 0 | case OP_ITER_INIT: return simple_instruction("OP_ITER_INIT", offset); |
251 | 0 | case OP_ITER_NEXT: return jump_instruction("OP_ITER_NEXT", 1, c, offset); |
252 | 0 | case OP_BUILD_ARRAY: return byte_instruction("OP_BUILD_ARRAY", c, offset); |
253 | 0 | case OP_ARRAY_FLATTEN: return simple_instruction("OP_ARRAY_FLATTEN", offset); |
254 | 0 | case OP_BUILD_MAP: return byte_instruction("OP_BUILD_MAP", c, offset); |
255 | 0 | case OP_BUILD_TUPLE: return byte_instruction("OP_BUILD_TUPLE", c, offset); |
256 | 0 | case OP_BUILD_STRUCT: return build_struct_instruction(c, offset); |
257 | 0 | case OP_BUILD_RANGE: return simple_instruction("OP_BUILD_RANGE", offset); |
258 | 0 | case OP_BUILD_ENUM: return build_enum_instruction(c, offset); |
259 | 0 | case OP_INDEX: return simple_instruction("OP_INDEX", offset); |
260 | 0 | case OP_SET_INDEX: return simple_instruction("OP_SET_INDEX", offset); |
261 | 0 | case OP_GET_FIELD: return constant_instruction("OP_GET_FIELD", c, offset); |
262 | 0 | case OP_SET_FIELD: return constant_instruction("OP_SET_FIELD", c, offset); |
263 | 0 | case OP_INVOKE: return invoke_instruction(c, offset); |
264 | 0 | case OP_INVOKE_LOCAL: { |
265 | 0 | uint8_t slot = c->code[offset + 1]; |
266 | 0 | uint8_t method_idx = c->code[offset + 2]; |
267 | 0 | uint8_t argc = c->code[offset + 3]; |
268 | 0 | fprintf(stderr, "%-20s slot=%d '", "OP_INVOKE_LOCAL", slot); |
269 | 0 | if (method_idx < c->const_len) { |
270 | 0 | char *repr = value_repr(&c->constants[method_idx]); |
271 | 0 | fprintf(stderr, "%s", repr); |
272 | 0 | free(repr); |
273 | 0 | } |
274 | 0 | fprintf(stderr, "' (%d args)\n", argc); |
275 | 0 | return offset + 4; |
276 | 0 | } |
277 | 0 | case OP_INVOKE_GLOBAL: { |
278 | 0 | uint8_t name_idx = c->code[offset + 1]; |
279 | 0 | uint8_t method_idx = c->code[offset + 2]; |
280 | 0 | uint8_t argc = c->code[offset + 3]; |
281 | 0 | fprintf(stderr, "%-20s '", "OP_INVOKE_GLOBAL"); |
282 | 0 | if (name_idx < c->const_len) { |
283 | 0 | char *repr = value_repr(&c->constants[name_idx]); |
284 | 0 | fprintf(stderr, "%s", repr); |
285 | 0 | free(repr); |
286 | 0 | } |
287 | 0 | fprintf(stderr, "'.'"); |
288 | 0 | if (method_idx < c->const_len) { |
289 | 0 | char *repr = value_repr(&c->constants[method_idx]); |
290 | 0 | fprintf(stderr, "%s", repr); |
291 | 0 | free(repr); |
292 | 0 | } |
293 | 0 | fprintf(stderr, "' (%d args)\n", argc); |
294 | 0 | return offset + 4; |
295 | 0 | } |
296 | 0 | case OP_SET_INDEX_LOCAL: return byte_instruction("OP_SET_INDEX_LOCAL", c, offset); |
297 | 0 | case OP_PUSH_EXCEPTION_HANDLER: return jump_instruction("OP_PUSH_EXCEPTION_HANDLER", 1, c, offset); |
298 | 0 | case OP_POP_EXCEPTION_HANDLER: return simple_instruction("OP_POP_EXCEPTION_HANDLER", offset); |
299 | 0 | case OP_THROW: return simple_instruction("OP_THROW", offset); |
300 | 0 | case OP_TRY_UNWRAP: return simple_instruction("OP_TRY_UNWRAP", offset); |
301 | 0 | case OP_DEFER_PUSH: return jump_instruction("OP_DEFER_PUSH", 1, c, offset); |
302 | 0 | case OP_DEFER_RUN: return simple_instruction("OP_DEFER_RUN", offset); |
303 | 0 | case OP_FREEZE: return simple_instruction("OP_FREEZE", offset); |
304 | 0 | case OP_THAW: return simple_instruction("OP_THAW", offset); |
305 | 0 | case OP_CLONE: return simple_instruction("OP_CLONE", offset); |
306 | 0 | case OP_MARK_FLUID: return simple_instruction("OP_MARK_FLUID", offset); |
307 | 0 | case OP_PRINT: return byte_instruction("OP_PRINT", c, offset); |
308 | 0 | case OP_IMPORT: return byte_instruction("OP_IMPORT", c, offset); |
309 | 0 | case OP_SCOPE: { |
310 | 0 | uint8_t spawn_count = c->code[offset + 1]; |
311 | 0 | uint8_t sync_idx = c->code[offset + 2]; |
312 | 0 | fprintf(stderr, "%-20s spawns=%d sync=%d", "OP_SCOPE", spawn_count, sync_idx); |
313 | 0 | for (uint8_t i = 0; i < spawn_count; i++) |
314 | 0 | fprintf(stderr, " spawn[%d]=%d", i, c->code[offset + 3 + i]); |
315 | 0 | fprintf(stderr, "\n"); |
316 | 0 | return offset + 3 + spawn_count; |
317 | 0 | } |
318 | 0 | case OP_SELECT: { |
319 | 0 | uint8_t arm_count = c->code[offset + 1]; |
320 | 0 | fprintf(stderr, "%-20s arms=%d\n", "OP_SELECT", arm_count); |
321 | 0 | size_t pos = offset + 2; |
322 | 0 | for (uint8_t i = 0; i < arm_count; i++) { |
323 | 0 | uint8_t flags = c->code[pos++]; |
324 | 0 | uint8_t chan_idx = c->code[pos++]; |
325 | 0 | uint8_t body_idx = c->code[pos++]; |
326 | 0 | uint8_t bind_idx = c->code[pos++]; |
327 | 0 | fprintf(stderr, " | arm %d: flags=%02x chan=%d body=%d bind=%d\n", |
328 | 0 | i, flags, chan_idx, body_idx, bind_idx); |
329 | 0 | } |
330 | 0 | return pos; |
331 | 0 | } |
332 | 0 | case OP_INC_LOCAL: return byte_instruction("OP_INC_LOCAL", c, offset); |
333 | 0 | case OP_DEC_LOCAL: return byte_instruction("OP_DEC_LOCAL", c, offset); |
334 | 0 | case OP_ADD_INT: return simple_instruction("OP_ADD_INT", offset); |
335 | 0 | case OP_SUB_INT: return simple_instruction("OP_SUB_INT", offset); |
336 | 0 | case OP_MUL_INT: return simple_instruction("OP_MUL_INT", offset); |
337 | 0 | case OP_LT_INT: return simple_instruction("OP_LT_INT", offset); |
338 | 0 | case OP_LTEQ_INT: return simple_instruction("OP_LTEQ_INT", offset); |
339 | 0 | case OP_LOAD_INT8: return byte_instruction("OP_LOAD_INT8", c, offset); |
340 | 0 | case OP_CONSTANT_16: return constant_instruction_16("OP_CONSTANT_16", c, offset); |
341 | 0 | case OP_GET_GLOBAL_16: return constant_instruction_16("OP_GET_GLOBAL_16", c, offset); |
342 | 0 | case OP_SET_GLOBAL_16: return constant_instruction_16("OP_SET_GLOBAL_16", c, offset); |
343 | 0 | case OP_DEFINE_GLOBAL_16: return constant_instruction_16("OP_DEFINE_GLOBAL_16", c, offset); |
344 | 0 | case OP_CLOSURE_16: { |
345 | 0 | uint16_t idx = (uint16_t)(c->code[offset + 1] << 8) | c->code[offset + 2]; |
346 | 0 | uint8_t uvc = c->code[offset + 3]; |
347 | 0 | fprintf(stderr, "%-20s %4d (upvalues: %d)\n", "OP_CLOSURE_16", idx, uvc); |
348 | 0 | return offset + 4 + uvc * 2; |
349 | 0 | } |
350 | 0 | case OP_RESET_EPHEMERAL: return simple_instruction("OP_RESET_EPHEMERAL", offset); |
351 | 0 | case OP_SET_LOCAL_POP: return byte_instruction("OP_SET_LOCAL_POP", c, offset); |
352 | 0 | case OP_HALT: return simple_instruction("OP_HALT", offset); |
353 | 0 | default: |
354 | 0 | fprintf(stderr, "Unknown opcode %d\n", op); |
355 | 0 | return offset + 1; |
356 | 0 | } |
357 | 0 | } |
358 | | |
359 | 0 | void chunk_disassemble(const Chunk *c, const char *name) { |
360 | 0 | fprintf(stderr, "== %s ==\n", name); |
361 | 0 | for (size_t offset = 0; offset < c->code_len; ) |
362 | 0 | offset = chunk_disassemble_instruction(c, offset); |
363 | 0 | } |