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, PushScope,
27 PopScope,
29
30 Add,
32 Sub,
33 Mul,
34 Div,
35 Mod,
36 Negate,
37
38 Equal,
40 NotEqual,
41 Less,
42 Greater,
43 LessEqual,
44 GreaterEqual,
45
46 Not,
48
49 Jump,
52 JumpIfFalse,
54 JumpIfTrue,
56 Pop,
58
59 Call,
62 TailCall,
66 Return,
68 Closure,
70
71 BuildList,
74 BuildDict,
76 Subscript,
78 Slice,
80
81 GetProperty,
84 GetPropertyOpt,
87 SetProperty,
90 SetSubscript,
93 MethodCall,
95 MethodCallOpt,
98
99 Concat,
102
103 IterInit,
106 IterNext,
109
110 Pipe,
113
114 Throw,
117 TryCatchSetup,
119 PopHandler,
121
122 Parallel,
126 ParallelMap,
129 ParallelSettle,
132 Spawn,
135
136 Import,
139 SelectiveImport,
141
142 DeadlineSetup,
145 DeadlineEnd,
147
148 BuildEnum,
153
154 MatchEnum,
159
160 PopIterator,
163
164 GetArgc,
167
168 CheckType,
174
175 TryUnwrap,
178
179 CallSpread,
182 MethodCallSpread,
185
186 Dup,
189 Swap,
191 Contains,
194
195 Yield,
197}
198
199#[derive(Debug, Clone, PartialEq)]
201pub enum Constant {
202 Int(i64),
203 Float(f64),
204 String(String),
205 Bool(bool),
206 Nil,
207 Duration(u64),
208}
209
210impl fmt::Display for Constant {
211 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
212 match self {
213 Constant::Int(n) => write!(f, "{n}"),
214 Constant::Float(n) => write!(f, "{n}"),
215 Constant::String(s) => write!(f, "\"{s}\""),
216 Constant::Bool(b) => write!(f, "{b}"),
217 Constant::Nil => write!(f, "nil"),
218 Constant::Duration(ms) => write!(f, "{ms}ms"),
219 }
220 }
221}
222
223#[derive(Debug, Clone)]
225pub struct Chunk {
226 pub code: Vec<u8>,
228 pub constants: Vec<Constant>,
230 pub lines: Vec<u32>,
232 pub columns: Vec<u32>,
235 pub source_file: Option<String>,
240 current_col: u32,
242 pub functions: Vec<CompiledFunction>,
244}
245
246#[derive(Debug, Clone)]
248pub struct CompiledFunction {
249 pub name: String,
250 pub params: Vec<String>,
251 pub default_start: Option<usize>,
253 pub chunk: Chunk,
254 pub is_generator: bool,
256 pub has_rest_param: bool,
258}
259
260impl Chunk {
261 pub fn new() -> Self {
262 Self {
263 code: Vec::new(),
264 constants: Vec::new(),
265 lines: Vec::new(),
266 columns: Vec::new(),
267 source_file: None,
268 current_col: 0,
269 functions: Vec::new(),
270 }
271 }
272
273 pub fn set_column(&mut self, col: u32) {
275 self.current_col = col;
276 }
277
278 pub fn add_constant(&mut self, constant: Constant) -> u16 {
280 for (i, c) in self.constants.iter().enumerate() {
282 if c == &constant {
283 return i as u16;
284 }
285 }
286 let idx = self.constants.len();
287 self.constants.push(constant);
288 idx as u16
289 }
290
291 pub fn emit(&mut self, op: Op, line: u32) {
293 let col = self.current_col;
294 self.code.push(op as u8);
295 self.lines.push(line);
296 self.columns.push(col);
297 }
298
299 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
301 let col = self.current_col;
302 self.code.push(op as u8);
303 self.code.push((arg >> 8) as u8);
304 self.code.push((arg & 0xFF) as u8);
305 self.lines.push(line);
306 self.lines.push(line);
307 self.lines.push(line);
308 self.columns.push(col);
309 self.columns.push(col);
310 self.columns.push(col);
311 }
312
313 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
315 let col = self.current_col;
316 self.code.push(op as u8);
317 self.code.push(arg);
318 self.lines.push(line);
319 self.lines.push(line);
320 self.columns.push(col);
321 self.columns.push(col);
322 }
323
324 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
326 self.emit_method_call_inner(Op::MethodCall, name_idx, arg_count, line);
327 }
328
329 pub fn emit_method_call_opt(&mut self, name_idx: u16, arg_count: u8, line: u32) {
331 self.emit_method_call_inner(Op::MethodCallOpt, name_idx, arg_count, line);
332 }
333
334 fn emit_method_call_inner(&mut self, op: Op, name_idx: u16, arg_count: u8, line: u32) {
335 let col = self.current_col;
336 self.code.push(op as u8);
337 self.code.push((name_idx >> 8) as u8);
338 self.code.push((name_idx & 0xFF) as u8);
339 self.code.push(arg_count);
340 self.lines.push(line);
341 self.lines.push(line);
342 self.lines.push(line);
343 self.lines.push(line);
344 self.columns.push(col);
345 self.columns.push(col);
346 self.columns.push(col);
347 self.columns.push(col);
348 }
349
350 pub fn current_offset(&self) -> usize {
352 self.code.len()
353 }
354
355 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
357 let col = self.current_col;
358 self.code.push(op as u8);
359 let patch_pos = self.code.len();
360 self.code.push(0xFF);
361 self.code.push(0xFF);
362 self.lines.push(line);
363 self.lines.push(line);
364 self.lines.push(line);
365 self.columns.push(col);
366 self.columns.push(col);
367 self.columns.push(col);
368 patch_pos
369 }
370
371 pub fn patch_jump(&mut self, patch_pos: usize) {
373 let target = self.code.len() as u16;
374 self.code[patch_pos] = (target >> 8) as u8;
375 self.code[patch_pos + 1] = (target & 0xFF) as u8;
376 }
377
378 pub fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
380 let target = target as u16;
381 self.code[patch_pos] = (target >> 8) as u8;
382 self.code[patch_pos + 1] = (target & 0xFF) as u8;
383 }
384
385 pub fn read_u16(&self, pos: usize) -> u16 {
387 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
388 }
389
390 pub fn disassemble(&self, name: &str) -> String {
392 let mut out = format!("== {name} ==\n");
393 let mut ip = 0;
394 while ip < self.code.len() {
395 let op = self.code[ip];
396 let line = self.lines.get(ip).copied().unwrap_or(0);
397 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
398 ip += 1;
399
400 match op {
401 x if x == Op::Constant as u8 => {
402 let idx = self.read_u16(ip);
403 ip += 2;
404 let val = &self.constants[idx as usize];
405 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
406 }
407 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
408 x if x == Op::True as u8 => out.push_str("TRUE\n"),
409 x if x == Op::False as u8 => out.push_str("FALSE\n"),
410 x if x == Op::GetVar as u8 => {
411 let idx = self.read_u16(ip);
412 ip += 2;
413 out.push_str(&format!(
414 "GET_VAR {:>4} ({})\n",
415 idx, self.constants[idx as usize]
416 ));
417 }
418 x if x == Op::DefLet as u8 => {
419 let idx = self.read_u16(ip);
420 ip += 2;
421 out.push_str(&format!(
422 "DEF_LET {:>4} ({})\n",
423 idx, self.constants[idx as usize]
424 ));
425 }
426 x if x == Op::DefVar as u8 => {
427 let idx = self.read_u16(ip);
428 ip += 2;
429 out.push_str(&format!(
430 "DEF_VAR {:>4} ({})\n",
431 idx, self.constants[idx as usize]
432 ));
433 }
434 x if x == Op::SetVar as u8 => {
435 let idx = self.read_u16(ip);
436 ip += 2;
437 out.push_str(&format!(
438 "SET_VAR {:>4} ({})\n",
439 idx, self.constants[idx as usize]
440 ));
441 }
442 x if x == Op::PushScope as u8 => out.push_str("PUSH_SCOPE\n"),
443 x if x == Op::PopScope as u8 => out.push_str("POP_SCOPE\n"),
444 x if x == Op::Add as u8 => out.push_str("ADD\n"),
445 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
446 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
447 x if x == Op::Div as u8 => out.push_str("DIV\n"),
448 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
449 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
450 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
451 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
452 x if x == Op::Less as u8 => out.push_str("LESS\n"),
453 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
454 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
455 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
456 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
457 x if x == Op::Not as u8 => out.push_str("NOT\n"),
458 x if x == Op::Jump as u8 => {
459 let target = self.read_u16(ip);
460 ip += 2;
461 out.push_str(&format!("JUMP {:>4}\n", target));
462 }
463 x if x == Op::JumpIfFalse as u8 => {
464 let target = self.read_u16(ip);
465 ip += 2;
466 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
467 }
468 x if x == Op::JumpIfTrue as u8 => {
469 let target = self.read_u16(ip);
470 ip += 2;
471 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
472 }
473 x if x == Op::Pop as u8 => out.push_str("POP\n"),
474 x if x == Op::Call as u8 => {
475 let argc = self.code[ip];
476 ip += 1;
477 out.push_str(&format!("CALL {:>4}\n", argc));
478 }
479 x if x == Op::TailCall as u8 => {
480 let argc = self.code[ip];
481 ip += 1;
482 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
483 }
484 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
485 x if x == Op::Closure as u8 => {
486 let idx = self.read_u16(ip);
487 ip += 2;
488 out.push_str(&format!("CLOSURE {:>4}\n", idx));
489 }
490 x if x == Op::BuildList as u8 => {
491 let count = self.read_u16(ip);
492 ip += 2;
493 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
494 }
495 x if x == Op::BuildDict as u8 => {
496 let count = self.read_u16(ip);
497 ip += 2;
498 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
499 }
500 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
501 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
502 x if x == Op::GetProperty as u8 => {
503 let idx = self.read_u16(ip);
504 ip += 2;
505 out.push_str(&format!(
506 "GET_PROPERTY {:>4} ({})\n",
507 idx, self.constants[idx as usize]
508 ));
509 }
510 x if x == Op::GetPropertyOpt as u8 => {
511 let idx = self.read_u16(ip);
512 ip += 2;
513 out.push_str(&format!(
514 "GET_PROPERTY_OPT {:>4} ({})\n",
515 idx, self.constants[idx as usize]
516 ));
517 }
518 x if x == Op::SetProperty as u8 => {
519 let idx = self.read_u16(ip);
520 ip += 2;
521 out.push_str(&format!(
522 "SET_PROPERTY {:>4} ({})\n",
523 idx, self.constants[idx as usize]
524 ));
525 }
526 x if x == Op::SetSubscript as u8 => {
527 let idx = self.read_u16(ip);
528 ip += 2;
529 out.push_str(&format!(
530 "SET_SUBSCRIPT {:>4} ({})\n",
531 idx, self.constants[idx as usize]
532 ));
533 }
534 x if x == Op::MethodCall as u8 => {
535 let idx = self.read_u16(ip);
536 ip += 2;
537 let argc = self.code[ip];
538 ip += 1;
539 out.push_str(&format!(
540 "METHOD_CALL {:>4} ({}) argc={}\n",
541 idx, self.constants[idx as usize], argc
542 ));
543 }
544 x if x == Op::MethodCallOpt as u8 => {
545 let idx = self.read_u16(ip);
546 ip += 2;
547 let argc = self.code[ip];
548 ip += 1;
549 out.push_str(&format!(
550 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
551 idx, self.constants[idx as usize], argc
552 ));
553 }
554 x if x == Op::Concat as u8 => {
555 let count = self.read_u16(ip);
556 ip += 2;
557 out.push_str(&format!("CONCAT {:>4}\n", count));
558 }
559 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
560 x if x == Op::IterNext as u8 => {
561 let target = self.read_u16(ip);
562 ip += 2;
563 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
564 }
565 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
566 x if x == Op::TryCatchSetup as u8 => {
567 let target = self.read_u16(ip);
568 ip += 2;
569 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
570 }
571 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
572 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
573 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
574 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
575 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
576 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
577 x if x == Op::Import as u8 => {
578 let idx = self.read_u16(ip);
579 ip += 2;
580 out.push_str(&format!(
581 "IMPORT {:>4} ({})\n",
582 idx, self.constants[idx as usize]
583 ));
584 }
585 x if x == Op::SelectiveImport as u8 => {
586 let path_idx = self.read_u16(ip);
587 ip += 2;
588 let names_idx = self.read_u16(ip);
589 ip += 2;
590 out.push_str(&format!(
591 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
592 path_idx,
593 self.constants[path_idx as usize],
594 names_idx,
595 self.constants[names_idx as usize]
596 ));
597 }
598 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
599 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
600 x if x == Op::BuildEnum as u8 => {
601 let enum_idx = self.read_u16(ip);
602 ip += 2;
603 let variant_idx = self.read_u16(ip);
604 ip += 2;
605 let field_count = self.read_u16(ip);
606 ip += 2;
607 out.push_str(&format!(
608 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
609 enum_idx,
610 self.constants[enum_idx as usize],
611 variant_idx,
612 self.constants[variant_idx as usize],
613 field_count
614 ));
615 }
616 x if x == Op::MatchEnum as u8 => {
617 let enum_idx = self.read_u16(ip);
618 ip += 2;
619 let variant_idx = self.read_u16(ip);
620 ip += 2;
621 out.push_str(&format!(
622 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
623 enum_idx,
624 self.constants[enum_idx as usize],
625 variant_idx,
626 self.constants[variant_idx as usize]
627 ));
628 }
629 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
630 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
631 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
632 x if x == Op::MethodCallSpread as u8 => {
633 let idx = self.read_u16(ip + 1);
634 ip += 2;
635 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
636 }
637 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
638 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
639 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
640 _ => {
641 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
642 }
643 }
644 }
645 out
646 }
647}
648
649impl Default for Chunk {
650 fn default() -> Self {
651 Self::new()
652 }
653}