Files
not-python-rust/src/disassemble.rs

171 lines
5.7 KiB
Rust
Raw Normal View History

use crate::obj::function::UserFunction;
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<ObjP>, globals: &Vec<String>) {
let mut rows: Vec<Row> = 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";
Revamp object system, start using `gc` crate Wow, what a ride. I think everything should be working now. In short: * Objects use the `gc` crate, which as a `Gc` garbage-collected pointer type. I may choose to implement my own in contiguous memory in the future. We will see. * The type system is no longer global. This is a bit of a burden, because now, whenever you want to create a new object, you need to pass its type object into the `Obj::instantiate` method, as well as its `::create` static method. * This burden is somewhat alleviated by the `ObjFactory` trait, which helps create new objects as long as you have access to a `builtins` hashmap. So something that would normally look like this: fn init_builtins(builtins: &mut HashMap<String, ObjP>) { let print_builtin = upcast_obj(BuiltinFunctionInst::create( ObjP::clone(&builtins.get("BuiltinFunction").unwrap()), "print", print, 1 ); builtins.insert("print".to_string(), print_builtin) // other builtins inserted here... } now looks like this: fn init_builtins(builtins: &mut HashMap<String, ObjP>) { let print_builtin = builtins.create_builtin_function("print", print, 1); builtins.insert("print".to_string(), print_builtin); } (turns out, if all you need is a HashMap<String, ObjP>, you can implement ObjFactory for HashMap<String, ObjP> itself(!)) Overall, I'm happier with this design, and I think this is what is going to get merged. It's a little weird to be querying type names that are used in the language itself to get those type objects, but whatever works, I guess. Next up is vtables. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-23 18:12:32 -07:00
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";
Revamp object system, start using `gc` crate Wow, what a ride. I think everything should be working now. In short: * Objects use the `gc` crate, which as a `Gc` garbage-collected pointer type. I may choose to implement my own in contiguous memory in the future. We will see. * The type system is no longer global. This is a bit of a burden, because now, whenever you want to create a new object, you need to pass its type object into the `Obj::instantiate` method, as well as its `::create` static method. * This burden is somewhat alleviated by the `ObjFactory` trait, which helps create new objects as long as you have access to a `builtins` hashmap. So something that would normally look like this: fn init_builtins(builtins: &mut HashMap<String, ObjP>) { let print_builtin = upcast_obj(BuiltinFunctionInst::create( ObjP::clone(&builtins.get("BuiltinFunction").unwrap()), "print", print, 1 ); builtins.insert("print".to_string(), print_builtin) // other builtins inserted here... } now looks like this: fn init_builtins(builtins: &mut HashMap<String, ObjP>) { let print_builtin = builtins.create_builtin_function("print", print, 1); builtins.insert("print".to_string(), print_builtin); } (turns out, if all you need is a HashMap<String, ObjP>, you can implement ObjFactory for HashMap<String, ObjP> itself(!)) Overall, I'm happier with this design, and I think this is what is going to get merged. It's a little weird to be querying type names that are used in the language itself to get those type objects, but whatever works, I guess. Next up is vtables. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-23 18:12:32 -07:00
arg = format!("{}", &constants[*constant_id as usize].borrow());
info = format!("(constant ID {constant_id})");
}
Op::SetAttr(constant_id) => {
op_str = "SET_ATTR";
Revamp object system, start using `gc` crate Wow, what a ride. I think everything should be working now. In short: * Objects use the `gc` crate, which as a `Gc` garbage-collected pointer type. I may choose to implement my own in contiguous memory in the future. We will see. * The type system is no longer global. This is a bit of a burden, because now, whenever you want to create a new object, you need to pass its type object into the `Obj::instantiate` method, as well as its `::create` static method. * This burden is somewhat alleviated by the `ObjFactory` trait, which helps create new objects as long as you have access to a `builtins` hashmap. So something that would normally look like this: fn init_builtins(builtins: &mut HashMap<String, ObjP>) { let print_builtin = upcast_obj(BuiltinFunctionInst::create( ObjP::clone(&builtins.get("BuiltinFunction").unwrap()), "print", print, 1 ); builtins.insert("print".to_string(), print_builtin) // other builtins inserted here... } now looks like this: fn init_builtins(builtins: &mut HashMap<String, ObjP>) { let print_builtin = builtins.create_builtin_function("print", print, 1); builtins.insert("print".to_string(), print_builtin); } (turns out, if all you need is a HashMap<String, ObjP>, you can implement ObjFactory for HashMap<String, ObjP> itself(!)) Overall, I'm happier with this design, and I think this is what is going to get merged. It's a little weird to be querying type names that are used in the language itself to get those type objects, but whatever works, I guess. Next up is vtables. Signed-off-by: Alek Ratzloff <alekratz@gmail.com>
2024-09-23 18:12:32 -07:00
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::BuildList(len) => {
op_str = "BUILD_LIST";
arg = format!("{len}");
info = String::new();
}
Op::Nop => {
op_str = "NOP";
arg = String::new();
info = String::new();
}
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<Row>) {
// 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<ObjP>, globals: &Vec<String>) {
println!("== main chunk");
println!();
disassemble_chunk(chunk, constants, globals);
for constant in constants {
if let Some(fun) = constant.borrow().as_any().downcast_ref::<UserFunction>() {
println!();
println!(
"== {} starting on line {}",
fun.name(),
fun.chunk().lines[0].0
);
println!();
disassemble_chunk(fun.chunk(), constants, globals);
}
}
}