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