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