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