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