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