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