1use std::fmt;
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq)]
5#[repr(u8)]
6pub enum Op {
7 Constant, Nil,
11 True,
13 False,
15
16 GetVar, DefLet, DefVar, SetVar, Add,
28 Sub,
29 Mul,
30 Div,
31 Mod,
32 Negate,
33
34 Equal,
36 NotEqual,
37 Less,
38 Greater,
39 LessEqual,
40 GreaterEqual,
41
42 Not,
44
45 Jump,
48 JumpIfFalse,
50 JumpIfTrue,
52 Pop,
54
55 Call,
58 TailCall,
62 Return,
64 Closure,
66
67 BuildList,
70 BuildDict,
72 Subscript,
74
75 GetProperty,
78 SetProperty,
81 SetSubscript,
84 MethodCall,
86
87 Concat,
90
91 IterInit,
94 IterNext,
97
98 Pipe,
101
102 Throw,
105 TryCatchSetup,
107 PopHandler,
109
110 Parallel,
114 ParallelMap,
117 Spawn,
120
121 Import,
124 SelectiveImport,
126
127 DeadlineSetup,
130 DeadlineEnd,
132
133 BuildEnum,
138
139 MatchEnum,
144
145 PopIterator,
148
149 Dup,
152 Swap,
154}
155
156#[derive(Debug, Clone, PartialEq)]
158pub enum Constant {
159 Int(i64),
160 Float(f64),
161 String(String),
162 Bool(bool),
163 Nil,
164 Duration(u64),
165}
166
167impl fmt::Display for Constant {
168 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
169 match self {
170 Constant::Int(n) => write!(f, "{n}"),
171 Constant::Float(n) => write!(f, "{n}"),
172 Constant::String(s) => write!(f, "\"{s}\""),
173 Constant::Bool(b) => write!(f, "{b}"),
174 Constant::Nil => write!(f, "nil"),
175 Constant::Duration(ms) => write!(f, "{ms}ms"),
176 }
177 }
178}
179
180#[derive(Debug, Clone)]
182pub struct Chunk {
183 pub code: Vec<u8>,
185 pub constants: Vec<Constant>,
187 pub lines: Vec<u32>,
189 pub functions: Vec<CompiledFunction>,
191}
192
193#[derive(Debug, Clone)]
195pub struct CompiledFunction {
196 pub name: String,
197 pub params: Vec<String>,
198 pub chunk: Chunk,
199}
200
201impl Chunk {
202 pub fn new() -> Self {
203 Self {
204 code: Vec::new(),
205 constants: Vec::new(),
206 lines: Vec::new(),
207 functions: Vec::new(),
208 }
209 }
210
211 pub fn add_constant(&mut self, constant: Constant) -> u16 {
213 for (i, c) in self.constants.iter().enumerate() {
215 if c == &constant {
216 return i as u16;
217 }
218 }
219 let idx = self.constants.len();
220 self.constants.push(constant);
221 idx as u16
222 }
223
224 pub fn emit(&mut self, op: Op, line: u32) {
226 self.code.push(op as u8);
227 self.lines.push(line);
228 }
229
230 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
232 self.code.push(op as u8);
233 self.code.push((arg >> 8) as u8);
234 self.code.push((arg & 0xFF) as u8);
235 self.lines.push(line);
236 self.lines.push(line);
237 self.lines.push(line);
238 }
239
240 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
242 self.code.push(op as u8);
243 self.code.push(arg);
244 self.lines.push(line);
245 self.lines.push(line);
246 }
247
248 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
250 self.code.push(Op::MethodCall as u8);
251 self.code.push((name_idx >> 8) as u8);
252 self.code.push((name_idx & 0xFF) as u8);
253 self.code.push(arg_count);
254 self.lines.push(line);
255 self.lines.push(line);
256 self.lines.push(line);
257 self.lines.push(line);
258 }
259
260 pub fn current_offset(&self) -> usize {
262 self.code.len()
263 }
264
265 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
267 self.code.push(op as u8);
268 let patch_pos = self.code.len();
269 self.code.push(0xFF);
270 self.code.push(0xFF);
271 self.lines.push(line);
272 self.lines.push(line);
273 self.lines.push(line);
274 patch_pos
275 }
276
277 pub fn patch_jump(&mut self, patch_pos: usize) {
279 let target = self.code.len() as u16;
280 self.code[patch_pos] = (target >> 8) as u8;
281 self.code[patch_pos + 1] = (target & 0xFF) as u8;
282 }
283
284 pub fn read_u16(&self, pos: usize) -> u16 {
286 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
287 }
288
289 pub fn disassemble(&self, name: &str) -> String {
291 let mut out = format!("== {name} ==\n");
292 let mut ip = 0;
293 while ip < self.code.len() {
294 let op = self.code[ip];
295 let line = self.lines.get(ip).copied().unwrap_or(0);
296 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
297 ip += 1;
298
299 match op {
300 x if x == Op::Constant as u8 => {
301 let idx = self.read_u16(ip);
302 ip += 2;
303 let val = &self.constants[idx as usize];
304 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
305 }
306 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
307 x if x == Op::True as u8 => out.push_str("TRUE\n"),
308 x if x == Op::False as u8 => out.push_str("FALSE\n"),
309 x if x == Op::GetVar as u8 => {
310 let idx = self.read_u16(ip);
311 ip += 2;
312 out.push_str(&format!(
313 "GET_VAR {:>4} ({})\n",
314 idx, self.constants[idx as usize]
315 ));
316 }
317 x if x == Op::DefLet as u8 => {
318 let idx = self.read_u16(ip);
319 ip += 2;
320 out.push_str(&format!(
321 "DEF_LET {:>4} ({})\n",
322 idx, self.constants[idx as usize]
323 ));
324 }
325 x if x == Op::DefVar as u8 => {
326 let idx = self.read_u16(ip);
327 ip += 2;
328 out.push_str(&format!(
329 "DEF_VAR {:>4} ({})\n",
330 idx, self.constants[idx as usize]
331 ));
332 }
333 x if x == Op::SetVar as u8 => {
334 let idx = self.read_u16(ip);
335 ip += 2;
336 out.push_str(&format!(
337 "SET_VAR {:>4} ({})\n",
338 idx, self.constants[idx as usize]
339 ));
340 }
341 x if x == Op::Add as u8 => out.push_str("ADD\n"),
342 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
343 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
344 x if x == Op::Div as u8 => out.push_str("DIV\n"),
345 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
346 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
347 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
348 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
349 x if x == Op::Less as u8 => out.push_str("LESS\n"),
350 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
351 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
352 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
353 x if x == Op::Not as u8 => out.push_str("NOT\n"),
354 x if x == Op::Jump as u8 => {
355 let target = self.read_u16(ip);
356 ip += 2;
357 out.push_str(&format!("JUMP {:>4}\n", target));
358 }
359 x if x == Op::JumpIfFalse as u8 => {
360 let target = self.read_u16(ip);
361 ip += 2;
362 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
363 }
364 x if x == Op::JumpIfTrue as u8 => {
365 let target = self.read_u16(ip);
366 ip += 2;
367 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
368 }
369 x if x == Op::Pop as u8 => out.push_str("POP\n"),
370 x if x == Op::Call as u8 => {
371 let argc = self.code[ip];
372 ip += 1;
373 out.push_str(&format!("CALL {:>4}\n", argc));
374 }
375 x if x == Op::TailCall as u8 => {
376 let argc = self.code[ip];
377 ip += 1;
378 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
379 }
380 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
381 x if x == Op::Closure as u8 => {
382 let idx = self.read_u16(ip);
383 ip += 2;
384 out.push_str(&format!("CLOSURE {:>4}\n", idx));
385 }
386 x if x == Op::BuildList as u8 => {
387 let count = self.read_u16(ip);
388 ip += 2;
389 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
390 }
391 x if x == Op::BuildDict as u8 => {
392 let count = self.read_u16(ip);
393 ip += 2;
394 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
395 }
396 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
397 x if x == Op::GetProperty as u8 => {
398 let idx = self.read_u16(ip);
399 ip += 2;
400 out.push_str(&format!(
401 "GET_PROPERTY {:>4} ({})\n",
402 idx, self.constants[idx as usize]
403 ));
404 }
405 x if x == Op::SetProperty as u8 => {
406 let idx = self.read_u16(ip);
407 ip += 2;
408 out.push_str(&format!(
409 "SET_PROPERTY {:>4} ({})\n",
410 idx, self.constants[idx as usize]
411 ));
412 }
413 x if x == Op::SetSubscript as u8 => {
414 let idx = self.read_u16(ip);
415 ip += 2;
416 out.push_str(&format!(
417 "SET_SUBSCRIPT {:>4} ({})\n",
418 idx, self.constants[idx as usize]
419 ));
420 }
421 x if x == Op::MethodCall as u8 => {
422 let idx = self.read_u16(ip);
423 ip += 2;
424 let argc = self.code[ip];
425 ip += 1;
426 out.push_str(&format!(
427 "METHOD_CALL {:>4} ({}) argc={}\n",
428 idx, self.constants[idx as usize], argc
429 ));
430 }
431 x if x == Op::Concat as u8 => {
432 let count = self.read_u16(ip);
433 ip += 2;
434 out.push_str(&format!("CONCAT {:>4}\n", count));
435 }
436 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
437 x if x == Op::IterNext as u8 => {
438 let target = self.read_u16(ip);
439 ip += 2;
440 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
441 }
442 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
443 x if x == Op::TryCatchSetup as u8 => {
444 let target = self.read_u16(ip);
445 ip += 2;
446 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
447 }
448 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
449 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
450 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
451 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
452 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
453 x if x == Op::Import as u8 => {
454 let idx = self.read_u16(ip);
455 ip += 2;
456 out.push_str(&format!(
457 "IMPORT {:>4} ({})\n",
458 idx, self.constants[idx as usize]
459 ));
460 }
461 x if x == Op::SelectiveImport as u8 => {
462 let path_idx = self.read_u16(ip);
463 ip += 2;
464 let names_idx = self.read_u16(ip);
465 ip += 2;
466 out.push_str(&format!(
467 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
468 path_idx,
469 self.constants[path_idx as usize],
470 names_idx,
471 self.constants[names_idx as usize]
472 ));
473 }
474 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
475 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
476 x if x == Op::BuildEnum as u8 => {
477 let enum_idx = self.read_u16(ip);
478 ip += 2;
479 let variant_idx = self.read_u16(ip);
480 ip += 2;
481 let field_count = self.read_u16(ip);
482 ip += 2;
483 out.push_str(&format!(
484 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
485 enum_idx,
486 self.constants[enum_idx as usize],
487 variant_idx,
488 self.constants[variant_idx as usize],
489 field_count
490 ));
491 }
492 x if x == Op::MatchEnum as u8 => {
493 let enum_idx = self.read_u16(ip);
494 ip += 2;
495 let variant_idx = self.read_u16(ip);
496 ip += 2;
497 out.push_str(&format!(
498 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
499 enum_idx,
500 self.constants[enum_idx as usize],
501 variant_idx,
502 self.constants[variant_idx as usize]
503 ));
504 }
505 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
506 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
507 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
508 _ => {
509 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
510 }
511 }
512 }
513 out
514 }
515}
516
517impl Default for Chunk {
518 fn default() -> Self {
519 Self::new()
520 }
521}