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