use crate::obj::function::UserFunctionInst; use crate::obj::ObjP; use crate::vm::{Chunk, JumpOpArg, Op}; type Row = (String, String, &'static str, String, String); fn disassemble_chunk(chunk: &Chunk, constants: &Vec, globals: &Vec) { let mut rows: Vec = vec![( "ADDR".into(), "LINE".into(), "OP".into(), "ARG".into(), "INFO".into(), )]; for (index, op) in chunk.code.iter().enumerate() { let (start_line, end_line) = chunk.lines[index]; let addr: String = index.to_string(); let line = if start_line == end_line { start_line.to_string() } else { format!("{start_line}-{end_line}") }; let op_str: &'static str; let arg: String; let info: String; match op { Op::Pop => { op_str = "POP"; arg = String::new(); info = String::new(); } Op::PushConstant(constant_id) => { op_str = "PUSH_CONSTANT"; arg = format!("{}", &constants[*constant_id as usize].borrow()); info = format!("(constant ID {constant_id})"); } Op::GetLocal(local_id) => { op_str = "GET_LOCAL"; let local = &chunk.locals[*local_id as usize]; arg = local.name.to_string(); info = format!("(slot {}, local ID {})", local.slot, local.index); } Op::SetLocal(local_id) => { op_str = "SET_LOCAL"; let local = &chunk.locals[*local_id as usize]; arg = local.name.to_string(); info = format!("(slot {}, local ID {})", local.slot, local.index); } Op::GetGlobal(global_id) => { op_str = "GET_GLOBAL"; arg = globals[*global_id as usize].clone(); info = format!("(global ID {global_id})"); } Op::SetGlobal(global_id) => { op_str = "SET_GLOBAL"; arg = globals[*global_id as usize].clone(); info = format!("(global ID {global_id})"); } Op::GetAttr(constant_id) => { op_str = "GET_ATTR"; arg = format!("{}", &constants[*constant_id as usize].borrow()); info = format!("(constant ID {constant_id})"); } Op::SetAttr(constant_id) => { op_str = "SET_ATTR"; arg = format!("{}", &constants[*constant_id as usize].borrow()); info = format!("(constant ID {constant_id})"); } Op::Jump(jump_offset) => { op_str = "JUMP"; arg = format!("{}", jump_offset); info = format!("(address {})", (index as JumpOpArg) + *jump_offset); } Op::JumpFalse(jump_offset) => { op_str = "JUMP_FALSE"; arg = format!("{}", jump_offset); info = format!("(address {})", (index as JumpOpArg) + *jump_offset); } Op::JumpTrue(jump_offset) => { op_str = "JUMP_FALSE"; arg = format!("{}", jump_offset); info = format!("(address {})", (index as JumpOpArg) + *jump_offset); } Op::Call(argc) => { op_str = "CALL"; arg = format!("argc {argc}"); info = String::new(); } Op::Return => { op_str = "RETURN"; arg = String::new(); info = String::new(); } Op::CloseOver { depth, slot } => { op_str = "CLOSE_OVER"; arg = format!("{depth}"); info = format!("slot {slot} (name unknown)"); } Op::Halt => { op_str = "HALT"; arg = String::new(); info = String::new(); } } rows.push((addr, line, op_str, arg, info)); } display_rows(&rows); } fn display_rows(rows: &Vec) { // get the longest width of each row let mut addr_width = 0; let mut line_width = 0; let mut op_width = 0; let mut arg_width = 0; let mut info_width = 0; for (addr, line, op, arg, info) in rows { addr_width = addr_width.max(addr.len()); line_width = line_width.max(line.len()); op_width = op_width.max(op.len()); arg_width = arg_width.max(arg.len()); info_width = info_width.max(info.len()); } addr_width += 2; line_width += 2; op_width += 2; arg_width += 2; info_width += 2; for (addr, line, op, arg, info) in rows { println!( "{addr:>addr_width$} {line:>line_width$} {op:>op_width$} {arg:arg_width$} {info:info_width$}" ); } } pub fn disassemble(chunk: &Chunk, constants: &Vec, globals: &Vec) { println!("== main chunk"); println!(); disassemble_chunk(chunk, constants, globals); for constant in constants { if let Some(fun) = constant .borrow() .as_any() .downcast_ref::() { println!(); println!( "== {} starting on line {}", fun.name(), fun.chunk().lines[0].0 ); println!(); disassemble_chunk(fun.chunk(), constants, globals); } } }