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