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