1
0

Improve chunk disassembly code

This commit is contained in:
Jeff 2024-11-05 17:35:54 -05:00
parent 80b6380255
commit b0af1609f0

View File

@ -146,7 +146,9 @@ impl Chunk {
impl Display for Chunk { impl Display for Chunk {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.disassembler().styled(true).disassemble()) let disassembler = self.disassembler().styled(false);
write!(f, "{}", disassembler.disassemble())
} }
} }
@ -203,7 +205,7 @@ impl Local {
pub struct Scope { pub struct Scope {
/// The level of block nesting. /// The level of block nesting.
pub depth: u8, pub depth: u8,
/// The nth scope in the block. /// The nth scope in the chunk.
pub width: u8, pub width: u8,
} }
@ -228,6 +230,7 @@ impl Display for Scope {
} }
pub struct ChunkDisassembler<'a> { pub struct ChunkDisassembler<'a> {
output: String,
chunk: &'a Chunk, chunk: &'a Chunk,
source: Option<&'a str>, source: Option<&'a str>,
width: usize, width: usize,
@ -250,7 +253,7 @@ impl<'a> ChunkDisassembler<'a> {
"Locals", "Locals",
"------", "------",
"INDEX IDENTIFIER TYPE MUTABLE SCOPE REGISTER", "INDEX IDENTIFIER TYPE MUTABLE SCOPE REGISTER",
"----- ---------- -------- ------- ----- --------", "----- ---------- -------- ------- ------- --------",
]; ];
/// The default width of the disassembly output. To correctly align the output, this should /// The default width of the disassembly output. To correctly align the output, this should
@ -263,6 +266,7 @@ impl<'a> ChunkDisassembler<'a> {
pub fn new(chunk: &'a Chunk) -> Self { pub fn new(chunk: &'a Chunk) -> Self {
Self { Self {
output: String::new(),
chunk, chunk,
source: None, source: None,
width: Self::default_width(), width: Self::default_width(),
@ -271,38 +275,44 @@ impl<'a> ChunkDisassembler<'a> {
} }
} }
pub fn source(&mut self, source: &'a str) -> &mut Self { pub fn source(mut self, source: &'a str) -> Self {
self.source = Some(source); self.source = Some(source);
self self
} }
pub fn width(&mut self, width: usize) -> &mut Self { pub fn width(mut self, width: usize) -> Self {
self.width = width; self.width = width;
self self
} }
pub fn styled(&mut self, styled: bool) -> &mut Self { pub fn styled(mut self, styled: bool) -> Self {
self.styled = styled; self.styled = styled;
self self
} }
pub fn disassemble(&self) -> String { pub fn indent(mut self, indent: usize) -> Self {
#[allow(clippy::too_many_arguments)] self.indent = indent;
self
}
fn push( fn push(
&mut self,
text: &str, text: &str,
disassembly: &mut String,
width: usize,
indent: usize,
center: bool, center: bool,
style_bold: bool, style_bold: bool,
style_dim: bool, style_dim: bool,
add_border: bool, add_border: bool,
) { ) {
let characters = text.chars().collect::<Vec<char>>(); let characters = text.chars().collect::<Vec<char>>();
let content_width = if add_border { width - 2 } else { width }; let content_width = if add_border {
self.width - 2
} else {
self.width
};
let (line_characters, remainder) = characters let (line_characters, remainder) = characters
.split_at_checked(content_width) .split_at_checked(content_width)
.unwrap_or((characters.as_slice(), &[])); .unwrap_or((characters.as_slice(), &[]));
@ -330,39 +340,37 @@ impl<'a> ChunkDisassembler<'a> {
} else { } else {
line_characters.iter().collect::<String>() line_characters.iter().collect::<String>()
}; };
let length_before_content = disassembly.chars().count(); let length_before_content = self.output.chars().count();
for _ in 0..indent { for _ in 0..self.indent {
disassembly.push_str(""); self.output.push_str("");
} }
if add_border { if add_border {
disassembly.push('│'); self.output.push('│');
} }
disassembly.push_str(&" ".repeat(left_pad_length)); self.output.push_str(&" ".repeat(left_pad_length));
disassembly.push_str(&content); self.output.push_str(&content);
disassembly.push_str(&" ".repeat(right_pad_length)); self.output.push_str(&" ".repeat(right_pad_length));
let length_after_content = disassembly.chars().count(); let length_after_content = self.output.chars().count();
let line_length = length_after_content - length_before_content; let line_length = length_after_content - length_before_content;
if line_length < content_width - 1 { if line_length < content_width - 1 {
disassembly.push_str(&" ".repeat(content_width - line_length)); self.output
.push_str(&" ".repeat(content_width - line_length));
} }
if add_border { if add_border {
disassembly.push('│'); self.output.push('│');
} }
disassembly.push('\n'); self.output.push('\n');
if !remainder.is_empty() { if !remainder.is_empty() {
push( self.push(
remainder.iter().collect::<String>().as_str(), remainder.iter().collect::<String>().as_str(),
disassembly,
width,
indent,
center, center,
style_bold, style_bold,
style_dim, style_dim,
@ -371,52 +379,26 @@ impl<'a> ChunkDisassembler<'a> {
} }
} }
let push_header = |header: &str, disassembly: &mut String| { fn push_header(&mut self, header: &str) {
push( self.push(header, true, self.styled, false, true);
header, }
disassembly,
self.width, fn push_details(&mut self, details: &str) {
self.indent, self.push(details, true, false, false, true);
true, }
self.styled,
false, fn push_border(&mut self, border: &str) {
true, self.push(border, false, false, false, false);
); }
};
let push_details = |details: &str, disassembly: &mut String| { pub fn disassemble(mut self) -> String {
push(
details,
disassembly,
self.width,
self.indent,
true,
false,
false,
true,
);
};
let push_border = |border: &str, disassembly: &mut String| {
push(
border,
disassembly,
self.width,
self.indent,
false,
false,
false,
false,
)
};
let push_function_disassembly = |function_disassembly: &str, disassembly: &mut String| {
disassembly.push_str(function_disassembly);
};
let mut disassembly = String::new();
let top_border = "".to_string() + &"".repeat(self.width - 2) + ""; let top_border = "".to_string() + &"".repeat(self.width - 2) + "";
let section_border = "".to_string() + &"".repeat(self.width - 2) + ""; let section_border = "".to_string() + &"".repeat(self.width - 2) + "";
let bottom_border = "".to_string() + &"".repeat(self.width - 2) + ""; let bottom_border = "".to_string() + &"".repeat(self.width - 2) + "";
let name_display = self let name_display = self
.chunk .chunk
.name() .name
.as_ref()
.map(|identifier| identifier.to_string()) .map(|identifier| identifier.to_string())
.unwrap_or_else(|| { .unwrap_or_else(|| {
current_exe() current_exe()
@ -424,8 +406,8 @@ impl<'a> ChunkDisassembler<'a> {
.unwrap_or("Chunk Disassembly".to_string()) .unwrap_or("Chunk Disassembly".to_string())
}); });
push_border(&top_border, &mut disassembly); self.push_border(&top_border);
push_header(&name_display, &mut disassembly); self.push_header(&name_display);
let info_line = format!( let info_line = format!(
"{} instructions, {} constants, {} locals", "{} instructions, {} constants, {} locals",
@ -434,19 +416,10 @@ impl<'a> ChunkDisassembler<'a> {
self.chunk.locals.len() self.chunk.locals.len()
); );
push( self.push(&info_line, true, false, false, true);
&info_line,
&mut disassembly,
self.width,
self.indent,
true,
false,
false,
true,
);
for line in &Self::INSTRUCTION_HEADER { for line in &Self::INSTRUCTION_HEADER {
push_header(line, &mut disassembly); self.push_header(line);
} }
for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() { for (index, (instruction, position)) in self.chunk.instructions.iter().enumerate() {
@ -457,13 +430,13 @@ impl<'a> ChunkDisassembler<'a> {
let instruction_display = let instruction_display =
format!("{index:<5} {bytecode:<08X} {operation:15} {info:25} {position:13}"); format!("{index:<5} {bytecode:<08X} {operation:15} {info:25} {position:13}");
push_details(&instruction_display, &mut disassembly); self.push_details(&instruction_display);
} }
push_border(&section_border, &mut disassembly); self.push_border(&section_border);
for line in &Self::LOCAL_HEADER { for line in &Self::LOCAL_HEADER {
push_header(line, &mut disassembly); self.push_header(line);
} }
for ( for (
@ -471,7 +444,7 @@ impl<'a> ChunkDisassembler<'a> {
Local { Local {
identifier_index, identifier_index,
r#type, r#type,
scope: depth, scope,
register_index, register_index,
is_mutable: mutable, is_mutable: mutable,
}, },
@ -488,42 +461,43 @@ impl<'a> ChunkDisassembler<'a> {
.map(|r#type| r#type.to_string()) .map(|r#type| r#type.to_string())
.unwrap_or("unknown".to_string()); .unwrap_or("unknown".to_string());
let local_display = format!( let local_display = format!(
"{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {depth:<5} {register_index:8}" "{index:<5} {identifier_display:10} {type_display:8} {mutable:7} {scope:7} {register_index:8}"
); );
push_details(&local_display, &mut disassembly); self.push_details(&local_display);
} }
push_border(&section_border, &mut disassembly); self.push_border(&section_border);
for line in &Self::CONSTANT_HEADER { for line in &Self::CONSTANT_HEADER {
push_header(line, &mut disassembly); self.push_header(line);
} }
for (index, value) in self.chunk.constants.iter().enumerate() { for (index, value) in self.chunk.constants.iter().enumerate() {
let constant_display = format!("{index:<5} {value:<5}"); let constant_display = format!("{index:<5} {value:<5}");
push_details(&constant_display, &mut disassembly); self.push_details(&constant_display);
if let Some(function_disassembly) = match value { if let Some(function_disassembly) = match value {
Value::Function(function) => Some({ Value::Function(function) => Some({
let mut disassembler = function.chunk().disassembler(); function
disassembler.indent = self.indent + 1; .chunk()
.disassembler()
disassembler.styled(self.styled); .styled(self.styled)
disassembler.disassemble() .indent(self.indent + 1)
.disassemble()
}), }),
Value::Primitive(_) => None, Value::Primitive(_) => None,
Value::Object(_) => None, Value::Object(_) => None,
} { } {
push_function_disassembly(&function_disassembly, &mut disassembly); self.output.push_str(&function_disassembly);
} }
} }
push_border(&bottom_border, &mut disassembly); self.push_border(&bottom_border);
let _ = disassembly.trim_end_matches('\n'); let _ = self.output.trim_end_matches('\n');
disassembly self.output
} }
} }