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