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 Pow,
37 Negate,
38
39 Equal,
41 NotEqual,
42 Less,
43 Greater,
44 LessEqual,
45 GreaterEqual,
46
47 Not,
49
50 Jump,
53 JumpIfFalse,
55 JumpIfTrue,
57 Pop,
59
60 Call,
63 TailCall,
67 Return,
69 Closure,
71
72 BuildList,
75 BuildDict,
77 Subscript,
79 Slice,
81
82 GetProperty,
85 GetPropertyOpt,
88 SetProperty,
91 SetSubscript,
94 MethodCall,
96 MethodCallOpt,
99
100 Concat,
103
104 IterInit,
107 IterNext,
110
111 Pipe,
114
115 Throw,
118 TryCatchSetup,
120 PopHandler,
122
123 Parallel,
127 ParallelMap,
130 ParallelSettle,
133 Spawn,
136
137 Import,
140 SelectiveImport,
142
143 DeadlineSetup,
146 DeadlineEnd,
148
149 BuildEnum,
154
155 MatchEnum,
160
161 PopIterator,
164
165 GetArgc,
168
169 CheckType,
175
176 TryUnwrap,
179
180 CallSpread,
183 MethodCallSpread,
186
187 Dup,
190 Swap,
192 Contains,
195
196 Yield,
198}
199
200#[derive(Debug, Clone, PartialEq)]
202pub enum Constant {
203 Int(i64),
204 Float(f64),
205 String(String),
206 Bool(bool),
207 Nil,
208 Duration(u64),
209}
210
211impl fmt::Display for Constant {
212 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
213 match self {
214 Constant::Int(n) => write!(f, "{n}"),
215 Constant::Float(n) => write!(f, "{n}"),
216 Constant::String(s) => write!(f, "\"{s}\""),
217 Constant::Bool(b) => write!(f, "{b}"),
218 Constant::Nil => write!(f, "nil"),
219 Constant::Duration(ms) => write!(f, "{ms}ms"),
220 }
221 }
222}
223
224#[derive(Debug, Clone)]
226pub struct Chunk {
227 pub code: Vec<u8>,
229 pub constants: Vec<Constant>,
231 pub lines: Vec<u32>,
233 pub columns: Vec<u32>,
236 pub source_file: Option<String>,
241 current_col: u32,
243 pub functions: Vec<CompiledFunction>,
245}
246
247#[derive(Debug, Clone)]
249pub struct CompiledFunction {
250 pub name: String,
251 pub params: Vec<String>,
252 pub default_start: Option<usize>,
254 pub chunk: Chunk,
255 pub is_generator: bool,
257 pub has_rest_param: bool,
259}
260
261impl Chunk {
262 pub fn new() -> Self {
263 Self {
264 code: Vec::new(),
265 constants: Vec::new(),
266 lines: Vec::new(),
267 columns: Vec::new(),
268 source_file: None,
269 current_col: 0,
270 functions: Vec::new(),
271 }
272 }
273
274 pub fn set_column(&mut self, col: u32) {
276 self.current_col = col;
277 }
278
279 pub fn add_constant(&mut self, constant: Constant) -> u16 {
281 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::Pow as u8 => out.push_str("POW\n"),
450 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
451 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
452 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
453 x if x == Op::Less as u8 => out.push_str("LESS\n"),
454 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
455 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
456 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
457 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
458 x if x == Op::Not as u8 => out.push_str("NOT\n"),
459 x if x == Op::Jump as u8 => {
460 let target = self.read_u16(ip);
461 ip += 2;
462 out.push_str(&format!("JUMP {:>4}\n", target));
463 }
464 x if x == Op::JumpIfFalse as u8 => {
465 let target = self.read_u16(ip);
466 ip += 2;
467 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
468 }
469 x if x == Op::JumpIfTrue as u8 => {
470 let target = self.read_u16(ip);
471 ip += 2;
472 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
473 }
474 x if x == Op::Pop as u8 => out.push_str("POP\n"),
475 x if x == Op::Call as u8 => {
476 let argc = self.code[ip];
477 ip += 1;
478 out.push_str(&format!("CALL {:>4}\n", argc));
479 }
480 x if x == Op::TailCall as u8 => {
481 let argc = self.code[ip];
482 ip += 1;
483 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
484 }
485 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
486 x if x == Op::Closure as u8 => {
487 let idx = self.read_u16(ip);
488 ip += 2;
489 out.push_str(&format!("CLOSURE {:>4}\n", idx));
490 }
491 x if x == Op::BuildList as u8 => {
492 let count = self.read_u16(ip);
493 ip += 2;
494 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
495 }
496 x if x == Op::BuildDict as u8 => {
497 let count = self.read_u16(ip);
498 ip += 2;
499 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
500 }
501 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
502 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
503 x if x == Op::GetProperty as u8 => {
504 let idx = self.read_u16(ip);
505 ip += 2;
506 out.push_str(&format!(
507 "GET_PROPERTY {:>4} ({})\n",
508 idx, self.constants[idx as usize]
509 ));
510 }
511 x if x == Op::GetPropertyOpt as u8 => {
512 let idx = self.read_u16(ip);
513 ip += 2;
514 out.push_str(&format!(
515 "GET_PROPERTY_OPT {:>4} ({})\n",
516 idx, self.constants[idx as usize]
517 ));
518 }
519 x if x == Op::SetProperty as u8 => {
520 let idx = self.read_u16(ip);
521 ip += 2;
522 out.push_str(&format!(
523 "SET_PROPERTY {:>4} ({})\n",
524 idx, self.constants[idx as usize]
525 ));
526 }
527 x if x == Op::SetSubscript as u8 => {
528 let idx = self.read_u16(ip);
529 ip += 2;
530 out.push_str(&format!(
531 "SET_SUBSCRIPT {:>4} ({})\n",
532 idx, self.constants[idx as usize]
533 ));
534 }
535 x if x == Op::MethodCall as u8 => {
536 let idx = self.read_u16(ip);
537 ip += 2;
538 let argc = self.code[ip];
539 ip += 1;
540 out.push_str(&format!(
541 "METHOD_CALL {:>4} ({}) argc={}\n",
542 idx, self.constants[idx as usize], argc
543 ));
544 }
545 x if x == Op::MethodCallOpt as u8 => {
546 let idx = self.read_u16(ip);
547 ip += 2;
548 let argc = self.code[ip];
549 ip += 1;
550 out.push_str(&format!(
551 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
552 idx, self.constants[idx as usize], argc
553 ));
554 }
555 x if x == Op::Concat as u8 => {
556 let count = self.read_u16(ip);
557 ip += 2;
558 out.push_str(&format!("CONCAT {:>4}\n", count));
559 }
560 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
561 x if x == Op::IterNext as u8 => {
562 let target = self.read_u16(ip);
563 ip += 2;
564 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
565 }
566 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
567 x if x == Op::TryCatchSetup as u8 => {
568 let target = self.read_u16(ip);
569 ip += 2;
570 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
571 }
572 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
573 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
574 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
575 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
576 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
577 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
578 x if x == Op::Import as u8 => {
579 let idx = self.read_u16(ip);
580 ip += 2;
581 out.push_str(&format!(
582 "IMPORT {:>4} ({})\n",
583 idx, self.constants[idx as usize]
584 ));
585 }
586 x if x == Op::SelectiveImport as u8 => {
587 let path_idx = self.read_u16(ip);
588 ip += 2;
589 let names_idx = self.read_u16(ip);
590 ip += 2;
591 out.push_str(&format!(
592 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
593 path_idx,
594 self.constants[path_idx as usize],
595 names_idx,
596 self.constants[names_idx as usize]
597 ));
598 }
599 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
600 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
601 x if x == Op::BuildEnum as u8 => {
602 let enum_idx = self.read_u16(ip);
603 ip += 2;
604 let variant_idx = self.read_u16(ip);
605 ip += 2;
606 let field_count = self.read_u16(ip);
607 ip += 2;
608 out.push_str(&format!(
609 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
610 enum_idx,
611 self.constants[enum_idx as usize],
612 variant_idx,
613 self.constants[variant_idx as usize],
614 field_count
615 ));
616 }
617 x if x == Op::MatchEnum as u8 => {
618 let enum_idx = self.read_u16(ip);
619 ip += 2;
620 let variant_idx = self.read_u16(ip);
621 ip += 2;
622 out.push_str(&format!(
623 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
624 enum_idx,
625 self.constants[enum_idx as usize],
626 variant_idx,
627 self.constants[variant_idx as usize]
628 ));
629 }
630 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
631 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
632 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
633 x if x == Op::MethodCallSpread as u8 => {
634 let idx = self.read_u16(ip + 1);
635 ip += 2;
636 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
637 }
638 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
639 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
640 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
641 _ => {
642 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
643 }
644 }
645 }
646 out
647 }
648}
649
650impl Default for Chunk {
651 fn default() -> Self {
652 Self::new()
653 }
654}