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}
257
258impl Chunk {
259 pub fn new() -> Self {
260 Self {
261 code: Vec::new(),
262 constants: Vec::new(),
263 lines: Vec::new(),
264 columns: Vec::new(),
265 source_file: None,
266 current_col: 0,
267 functions: Vec::new(),
268 }
269 }
270
271 pub fn set_column(&mut self, col: u32) {
273 self.current_col = col;
274 }
275
276 pub fn add_constant(&mut self, constant: Constant) -> u16 {
278 for (i, c) in self.constants.iter().enumerate() {
280 if c == &constant {
281 return i as u16;
282 }
283 }
284 let idx = self.constants.len();
285 self.constants.push(constant);
286 idx as u16
287 }
288
289 pub fn emit(&mut self, op: Op, line: u32) {
291 let col = self.current_col;
292 self.code.push(op as u8);
293 self.lines.push(line);
294 self.columns.push(col);
295 }
296
297 pub fn emit_u16(&mut self, op: Op, arg: u16, line: u32) {
299 let col = self.current_col;
300 self.code.push(op as u8);
301 self.code.push((arg >> 8) as u8);
302 self.code.push((arg & 0xFF) as u8);
303 self.lines.push(line);
304 self.lines.push(line);
305 self.lines.push(line);
306 self.columns.push(col);
307 self.columns.push(col);
308 self.columns.push(col);
309 }
310
311 pub fn emit_u8(&mut self, op: Op, arg: u8, line: u32) {
313 let col = self.current_col;
314 self.code.push(op as u8);
315 self.code.push(arg);
316 self.lines.push(line);
317 self.lines.push(line);
318 self.columns.push(col);
319 self.columns.push(col);
320 }
321
322 pub fn emit_method_call(&mut self, name_idx: u16, arg_count: u8, line: u32) {
324 self.emit_method_call_inner(Op::MethodCall, name_idx, arg_count, line);
325 }
326
327 pub fn emit_method_call_opt(&mut self, name_idx: u16, arg_count: u8, line: u32) {
329 self.emit_method_call_inner(Op::MethodCallOpt, name_idx, arg_count, line);
330 }
331
332 fn emit_method_call_inner(&mut self, op: Op, name_idx: u16, arg_count: u8, line: u32) {
333 let col = self.current_col;
334 self.code.push(op as u8);
335 self.code.push((name_idx >> 8) as u8);
336 self.code.push((name_idx & 0xFF) as u8);
337 self.code.push(arg_count);
338 self.lines.push(line);
339 self.lines.push(line);
340 self.lines.push(line);
341 self.lines.push(line);
342 self.columns.push(col);
343 self.columns.push(col);
344 self.columns.push(col);
345 self.columns.push(col);
346 }
347
348 pub fn current_offset(&self) -> usize {
350 self.code.len()
351 }
352
353 pub fn emit_jump(&mut self, op: Op, line: u32) -> usize {
355 let col = self.current_col;
356 self.code.push(op as u8);
357 let patch_pos = self.code.len();
358 self.code.push(0xFF);
359 self.code.push(0xFF);
360 self.lines.push(line);
361 self.lines.push(line);
362 self.lines.push(line);
363 self.columns.push(col);
364 self.columns.push(col);
365 self.columns.push(col);
366 patch_pos
367 }
368
369 pub fn patch_jump(&mut self, patch_pos: usize) {
371 let target = self.code.len() as u16;
372 self.code[patch_pos] = (target >> 8) as u8;
373 self.code[patch_pos + 1] = (target & 0xFF) as u8;
374 }
375
376 pub fn patch_jump_to(&mut self, patch_pos: usize, target: usize) {
378 let target = target as u16;
379 self.code[patch_pos] = (target >> 8) as u8;
380 self.code[patch_pos + 1] = (target & 0xFF) as u8;
381 }
382
383 pub fn read_u16(&self, pos: usize) -> u16 {
385 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
386 }
387
388 pub fn disassemble(&self, name: &str) -> String {
390 let mut out = format!("== {name} ==\n");
391 let mut ip = 0;
392 while ip < self.code.len() {
393 let op = self.code[ip];
394 let line = self.lines.get(ip).copied().unwrap_or(0);
395 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
396 ip += 1;
397
398 match op {
399 x if x == Op::Constant as u8 => {
400 let idx = self.read_u16(ip);
401 ip += 2;
402 let val = &self.constants[idx as usize];
403 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
404 }
405 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
406 x if x == Op::True as u8 => out.push_str("TRUE\n"),
407 x if x == Op::False as u8 => out.push_str("FALSE\n"),
408 x if x == Op::GetVar as u8 => {
409 let idx = self.read_u16(ip);
410 ip += 2;
411 out.push_str(&format!(
412 "GET_VAR {:>4} ({})\n",
413 idx, self.constants[idx as usize]
414 ));
415 }
416 x if x == Op::DefLet as u8 => {
417 let idx = self.read_u16(ip);
418 ip += 2;
419 out.push_str(&format!(
420 "DEF_LET {:>4} ({})\n",
421 idx, self.constants[idx as usize]
422 ));
423 }
424 x if x == Op::DefVar as u8 => {
425 let idx = self.read_u16(ip);
426 ip += 2;
427 out.push_str(&format!(
428 "DEF_VAR {:>4} ({})\n",
429 idx, self.constants[idx as usize]
430 ));
431 }
432 x if x == Op::SetVar as u8 => {
433 let idx = self.read_u16(ip);
434 ip += 2;
435 out.push_str(&format!(
436 "SET_VAR {:>4} ({})\n",
437 idx, self.constants[idx as usize]
438 ));
439 }
440 x if x == Op::PushScope as u8 => out.push_str("PUSH_SCOPE\n"),
441 x if x == Op::PopScope as u8 => out.push_str("POP_SCOPE\n"),
442 x if x == Op::Add as u8 => out.push_str("ADD\n"),
443 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
444 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
445 x if x == Op::Div as u8 => out.push_str("DIV\n"),
446 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
447 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
448 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
449 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
450 x if x == Op::Less as u8 => out.push_str("LESS\n"),
451 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
452 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
453 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
454 x if x == Op::Contains as u8 => out.push_str("CONTAINS\n"),
455 x if x == Op::Not as u8 => out.push_str("NOT\n"),
456 x if x == Op::Jump as u8 => {
457 let target = self.read_u16(ip);
458 ip += 2;
459 out.push_str(&format!("JUMP {:>4}\n", target));
460 }
461 x if x == Op::JumpIfFalse as u8 => {
462 let target = self.read_u16(ip);
463 ip += 2;
464 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
465 }
466 x if x == Op::JumpIfTrue as u8 => {
467 let target = self.read_u16(ip);
468 ip += 2;
469 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
470 }
471 x if x == Op::Pop as u8 => out.push_str("POP\n"),
472 x if x == Op::Call as u8 => {
473 let argc = self.code[ip];
474 ip += 1;
475 out.push_str(&format!("CALL {:>4}\n", argc));
476 }
477 x if x == Op::TailCall as u8 => {
478 let argc = self.code[ip];
479 ip += 1;
480 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
481 }
482 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
483 x if x == Op::Closure as u8 => {
484 let idx = self.read_u16(ip);
485 ip += 2;
486 out.push_str(&format!("CLOSURE {:>4}\n", idx));
487 }
488 x if x == Op::BuildList as u8 => {
489 let count = self.read_u16(ip);
490 ip += 2;
491 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
492 }
493 x if x == Op::BuildDict as u8 => {
494 let count = self.read_u16(ip);
495 ip += 2;
496 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
497 }
498 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
499 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
500 x if x == Op::GetProperty as u8 => {
501 let idx = self.read_u16(ip);
502 ip += 2;
503 out.push_str(&format!(
504 "GET_PROPERTY {:>4} ({})\n",
505 idx, self.constants[idx as usize]
506 ));
507 }
508 x if x == Op::GetPropertyOpt as u8 => {
509 let idx = self.read_u16(ip);
510 ip += 2;
511 out.push_str(&format!(
512 "GET_PROPERTY_OPT {:>4} ({})\n",
513 idx, self.constants[idx as usize]
514 ));
515 }
516 x if x == Op::SetProperty as u8 => {
517 let idx = self.read_u16(ip);
518 ip += 2;
519 out.push_str(&format!(
520 "SET_PROPERTY {:>4} ({})\n",
521 idx, self.constants[idx as usize]
522 ));
523 }
524 x if x == Op::SetSubscript as u8 => {
525 let idx = self.read_u16(ip);
526 ip += 2;
527 out.push_str(&format!(
528 "SET_SUBSCRIPT {:>4} ({})\n",
529 idx, self.constants[idx as usize]
530 ));
531 }
532 x if x == Op::MethodCall as u8 => {
533 let idx = self.read_u16(ip);
534 ip += 2;
535 let argc = self.code[ip];
536 ip += 1;
537 out.push_str(&format!(
538 "METHOD_CALL {:>4} ({}) argc={}\n",
539 idx, self.constants[idx as usize], argc
540 ));
541 }
542 x if x == Op::MethodCallOpt as u8 => {
543 let idx = self.read_u16(ip);
544 ip += 2;
545 let argc = self.code[ip];
546 ip += 1;
547 out.push_str(&format!(
548 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
549 idx, self.constants[idx as usize], argc
550 ));
551 }
552 x if x == Op::Concat as u8 => {
553 let count = self.read_u16(ip);
554 ip += 2;
555 out.push_str(&format!("CONCAT {:>4}\n", count));
556 }
557 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
558 x if x == Op::IterNext as u8 => {
559 let target = self.read_u16(ip);
560 ip += 2;
561 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
562 }
563 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
564 x if x == Op::TryCatchSetup as u8 => {
565 let target = self.read_u16(ip);
566 ip += 2;
567 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
568 }
569 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
570 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
571 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
572 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
573 x if x == Op::ParallelSettle as u8 => out.push_str("PARALLEL_SETTLE\n"),
574 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
575 x if x == Op::Import as u8 => {
576 let idx = self.read_u16(ip);
577 ip += 2;
578 out.push_str(&format!(
579 "IMPORT {:>4} ({})\n",
580 idx, self.constants[idx as usize]
581 ));
582 }
583 x if x == Op::SelectiveImport as u8 => {
584 let path_idx = self.read_u16(ip);
585 ip += 2;
586 let names_idx = self.read_u16(ip);
587 ip += 2;
588 out.push_str(&format!(
589 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
590 path_idx,
591 self.constants[path_idx as usize],
592 names_idx,
593 self.constants[names_idx as usize]
594 ));
595 }
596 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
597 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
598 x if x == Op::BuildEnum as u8 => {
599 let enum_idx = self.read_u16(ip);
600 ip += 2;
601 let variant_idx = self.read_u16(ip);
602 ip += 2;
603 let field_count = self.read_u16(ip);
604 ip += 2;
605 out.push_str(&format!(
606 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
607 enum_idx,
608 self.constants[enum_idx as usize],
609 variant_idx,
610 self.constants[variant_idx as usize],
611 field_count
612 ));
613 }
614 x if x == Op::MatchEnum as u8 => {
615 let enum_idx = self.read_u16(ip);
616 ip += 2;
617 let variant_idx = self.read_u16(ip);
618 ip += 2;
619 out.push_str(&format!(
620 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
621 enum_idx,
622 self.constants[enum_idx as usize],
623 variant_idx,
624 self.constants[variant_idx as usize]
625 ));
626 }
627 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
628 x if x == Op::TryUnwrap as u8 => out.push_str("TRY_UNWRAP\n"),
629 x if x == Op::CallSpread as u8 => out.push_str("CALL_SPREAD\n"),
630 x if x == Op::MethodCallSpread as u8 => {
631 let idx = self.read_u16(ip + 1);
632 ip += 2;
633 out.push_str(&format!("METHOD_CALL_SPREAD {idx}\n"));
634 }
635 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
636 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
637 x if x == Op::Yield as u8 => out.push_str("YIELD\n"),
638 _ => {
639 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
640 }
641 }
642 }
643 out
644 }
645}
646
647impl Default for Chunk {
648 fn default() -> Self {
649 Self::new()
650 }
651}