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