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 read_u16(&self, pos: usize) -> u16 {
333 ((self.code[pos] as u16) << 8) | (self.code[pos + 1] as u16)
334 }
335
336 pub fn disassemble(&self, name: &str) -> String {
338 let mut out = format!("== {name} ==\n");
339 let mut ip = 0;
340 while ip < self.code.len() {
341 let op = self.code[ip];
342 let line = self.lines.get(ip).copied().unwrap_or(0);
343 out.push_str(&format!("{:04} [{:>4}] ", ip, line));
344 ip += 1;
345
346 match op {
347 x if x == Op::Constant as u8 => {
348 let idx = self.read_u16(ip);
349 ip += 2;
350 let val = &self.constants[idx as usize];
351 out.push_str(&format!("CONSTANT {:>4} ({})\n", idx, val));
352 }
353 x if x == Op::Nil as u8 => out.push_str("NIL\n"),
354 x if x == Op::True as u8 => out.push_str("TRUE\n"),
355 x if x == Op::False as u8 => out.push_str("FALSE\n"),
356 x if x == Op::GetVar as u8 => {
357 let idx = self.read_u16(ip);
358 ip += 2;
359 out.push_str(&format!(
360 "GET_VAR {:>4} ({})\n",
361 idx, self.constants[idx as usize]
362 ));
363 }
364 x if x == Op::DefLet as u8 => {
365 let idx = self.read_u16(ip);
366 ip += 2;
367 out.push_str(&format!(
368 "DEF_LET {:>4} ({})\n",
369 idx, self.constants[idx as usize]
370 ));
371 }
372 x if x == Op::DefVar as u8 => {
373 let idx = self.read_u16(ip);
374 ip += 2;
375 out.push_str(&format!(
376 "DEF_VAR {:>4} ({})\n",
377 idx, self.constants[idx as usize]
378 ));
379 }
380 x if x == Op::SetVar as u8 => {
381 let idx = self.read_u16(ip);
382 ip += 2;
383 out.push_str(&format!(
384 "SET_VAR {:>4} ({})\n",
385 idx, self.constants[idx as usize]
386 ));
387 }
388 x if x == Op::Add as u8 => out.push_str("ADD\n"),
389 x if x == Op::Sub as u8 => out.push_str("SUB\n"),
390 x if x == Op::Mul as u8 => out.push_str("MUL\n"),
391 x if x == Op::Div as u8 => out.push_str("DIV\n"),
392 x if x == Op::Mod as u8 => out.push_str("MOD\n"),
393 x if x == Op::Negate as u8 => out.push_str("NEGATE\n"),
394 x if x == Op::Equal as u8 => out.push_str("EQUAL\n"),
395 x if x == Op::NotEqual as u8 => out.push_str("NOT_EQUAL\n"),
396 x if x == Op::Less as u8 => out.push_str("LESS\n"),
397 x if x == Op::Greater as u8 => out.push_str("GREATER\n"),
398 x if x == Op::LessEqual as u8 => out.push_str("LESS_EQUAL\n"),
399 x if x == Op::GreaterEqual as u8 => out.push_str("GREATER_EQUAL\n"),
400 x if x == Op::Not as u8 => out.push_str("NOT\n"),
401 x if x == Op::Jump as u8 => {
402 let target = self.read_u16(ip);
403 ip += 2;
404 out.push_str(&format!("JUMP {:>4}\n", target));
405 }
406 x if x == Op::JumpIfFalse as u8 => {
407 let target = self.read_u16(ip);
408 ip += 2;
409 out.push_str(&format!("JUMP_IF_FALSE {:>4}\n", target));
410 }
411 x if x == Op::JumpIfTrue as u8 => {
412 let target = self.read_u16(ip);
413 ip += 2;
414 out.push_str(&format!("JUMP_IF_TRUE {:>4}\n", target));
415 }
416 x if x == Op::Pop as u8 => out.push_str("POP\n"),
417 x if x == Op::Call as u8 => {
418 let argc = self.code[ip];
419 ip += 1;
420 out.push_str(&format!("CALL {:>4}\n", argc));
421 }
422 x if x == Op::TailCall as u8 => {
423 let argc = self.code[ip];
424 ip += 1;
425 out.push_str(&format!("TAIL_CALL {:>4}\n", argc));
426 }
427 x if x == Op::Return as u8 => out.push_str("RETURN\n"),
428 x if x == Op::Closure as u8 => {
429 let idx = self.read_u16(ip);
430 ip += 2;
431 out.push_str(&format!("CLOSURE {:>4}\n", idx));
432 }
433 x if x == Op::BuildList as u8 => {
434 let count = self.read_u16(ip);
435 ip += 2;
436 out.push_str(&format!("BUILD_LIST {:>4}\n", count));
437 }
438 x if x == Op::BuildDict as u8 => {
439 let count = self.read_u16(ip);
440 ip += 2;
441 out.push_str(&format!("BUILD_DICT {:>4}\n", count));
442 }
443 x if x == Op::Subscript as u8 => out.push_str("SUBSCRIPT\n"),
444 x if x == Op::Slice as u8 => out.push_str("SLICE\n"),
445 x if x == Op::GetProperty as u8 => {
446 let idx = self.read_u16(ip);
447 ip += 2;
448 out.push_str(&format!(
449 "GET_PROPERTY {:>4} ({})\n",
450 idx, self.constants[idx as usize]
451 ));
452 }
453 x if x == Op::GetPropertyOpt as u8 => {
454 let idx = self.read_u16(ip);
455 ip += 2;
456 out.push_str(&format!(
457 "GET_PROPERTY_OPT {:>4} ({})\n",
458 idx, self.constants[idx as usize]
459 ));
460 }
461 x if x == Op::SetProperty as u8 => {
462 let idx = self.read_u16(ip);
463 ip += 2;
464 out.push_str(&format!(
465 "SET_PROPERTY {:>4} ({})\n",
466 idx, self.constants[idx as usize]
467 ));
468 }
469 x if x == Op::SetSubscript as u8 => {
470 let idx = self.read_u16(ip);
471 ip += 2;
472 out.push_str(&format!(
473 "SET_SUBSCRIPT {:>4} ({})\n",
474 idx, self.constants[idx as usize]
475 ));
476 }
477 x if x == Op::MethodCall as u8 => {
478 let idx = self.read_u16(ip);
479 ip += 2;
480 let argc = self.code[ip];
481 ip += 1;
482 out.push_str(&format!(
483 "METHOD_CALL {:>4} ({}) argc={}\n",
484 idx, self.constants[idx as usize], argc
485 ));
486 }
487 x if x == Op::MethodCallOpt as u8 => {
488 let idx = self.read_u16(ip);
489 ip += 2;
490 let argc = self.code[ip];
491 ip += 1;
492 out.push_str(&format!(
493 "METHOD_CALL_OPT {:>4} ({}) argc={}\n",
494 idx, self.constants[idx as usize], argc
495 ));
496 }
497 x if x == Op::Concat as u8 => {
498 let count = self.read_u16(ip);
499 ip += 2;
500 out.push_str(&format!("CONCAT {:>4}\n", count));
501 }
502 x if x == Op::IterInit as u8 => out.push_str("ITER_INIT\n"),
503 x if x == Op::IterNext as u8 => {
504 let target = self.read_u16(ip);
505 ip += 2;
506 out.push_str(&format!("ITER_NEXT {:>4}\n", target));
507 }
508 x if x == Op::Throw as u8 => out.push_str("THROW\n"),
509 x if x == Op::TryCatchSetup as u8 => {
510 let target = self.read_u16(ip);
511 ip += 2;
512 out.push_str(&format!("TRY_CATCH_SETUP {:>4}\n", target));
513 }
514 x if x == Op::PopHandler as u8 => out.push_str("POP_HANDLER\n"),
515 x if x == Op::Pipe as u8 => out.push_str("PIPE\n"),
516 x if x == Op::Parallel as u8 => out.push_str("PARALLEL\n"),
517 x if x == Op::ParallelMap as u8 => out.push_str("PARALLEL_MAP\n"),
518 x if x == Op::Spawn as u8 => out.push_str("SPAWN\n"),
519 x if x == Op::Import as u8 => {
520 let idx = self.read_u16(ip);
521 ip += 2;
522 out.push_str(&format!(
523 "IMPORT {:>4} ({})\n",
524 idx, self.constants[idx as usize]
525 ));
526 }
527 x if x == Op::SelectiveImport as u8 => {
528 let path_idx = self.read_u16(ip);
529 ip += 2;
530 let names_idx = self.read_u16(ip);
531 ip += 2;
532 out.push_str(&format!(
533 "SELECTIVE_IMPORT {:>4} ({}) names: {:>4} ({})\n",
534 path_idx,
535 self.constants[path_idx as usize],
536 names_idx,
537 self.constants[names_idx as usize]
538 ));
539 }
540 x if x == Op::DeadlineSetup as u8 => out.push_str("DEADLINE_SETUP\n"),
541 x if x == Op::DeadlineEnd as u8 => out.push_str("DEADLINE_END\n"),
542 x if x == Op::BuildEnum as u8 => {
543 let enum_idx = self.read_u16(ip);
544 ip += 2;
545 let variant_idx = self.read_u16(ip);
546 ip += 2;
547 let field_count = self.read_u16(ip);
548 ip += 2;
549 out.push_str(&format!(
550 "BUILD_ENUM {:>4} ({}) {:>4} ({}) fields={}\n",
551 enum_idx,
552 self.constants[enum_idx as usize],
553 variant_idx,
554 self.constants[variant_idx as usize],
555 field_count
556 ));
557 }
558 x if x == Op::MatchEnum as u8 => {
559 let enum_idx = self.read_u16(ip);
560 ip += 2;
561 let variant_idx = self.read_u16(ip);
562 ip += 2;
563 out.push_str(&format!(
564 "MATCH_ENUM {:>4} ({}) {:>4} ({})\n",
565 enum_idx,
566 self.constants[enum_idx as usize],
567 variant_idx,
568 self.constants[variant_idx as usize]
569 ));
570 }
571 x if x == Op::PopIterator as u8 => out.push_str("POP_ITERATOR\n"),
572 x if x == Op::Dup as u8 => out.push_str("DUP\n"),
573 x if x == Op::Swap as u8 => out.push_str("SWAP\n"),
574 _ => {
575 out.push_str(&format!("UNKNOWN(0x{:02x})\n", op));
576 }
577 }
578 }
579 out
580 }
581}
582
583impl Default for Chunk {
584 fn default() -> Self {
585 Self::new()
586 }
587}