1use crate::InstructionReader;
2use koto_memory::Ptr;
3use koto_parser::{ConstantPool, KString, Span};
4use std::fmt::{self, Write};
5
6#[derive(Clone, Debug, Default, PartialEq, Eq)]
8pub struct DebugInfo {
9 source_map: Vec<(u32, Span)>,
10 pub source: String,
12}
13
14impl DebugInfo {
15 pub fn push(&mut self, ip: u32, span: Span) {
20 if let Some(entry) = self.source_map.last() {
21 if entry.1 == span {
22 return;
26 }
27 }
28 self.source_map.push((ip, span));
29 }
30
31 pub fn get_source_span(&self, ip: u32) -> Option<Span> {
33 let mut result = None;
37 for entry in self.source_map.iter() {
38 if entry.0 <= ip {
39 result = Some(entry.1);
40 } else {
41 break;
42 }
43 }
44 result
45 }
46}
47
48#[derive(Clone, Default, PartialEq)]
50pub struct Chunk {
51 pub bytes: Vec<u8>,
53 pub constants: ConstantPool,
55 pub path: Option<KString>,
57 pub debug_info: DebugInfo,
59}
60
61impl Chunk {
62 pub fn bytes_as_string(chunk: &Chunk) -> String {
64 let mut iter = chunk.bytes.iter();
65 let mut result = String::new();
66
67 'outer: loop {
68 for i in 1..=16 {
69 match iter.next() {
70 Some(byte) => write!(result, "{byte:02x}").ok(),
71 None => break 'outer,
72 };
73
74 if i < 16 {
75 result += " ";
76
77 if i % 4 == 0 {
78 result += " ";
79 }
80 }
81 }
82 result += "\n";
83 }
84
85 result
86 }
87
88 pub fn instructions_as_string(chunk: Ptr<Chunk>, source_lines: &[&str]) -> String {
90 let ip_width = 5 + chunk.bytes.len().ilog10() as usize;
91
92 let mut result = String::new();
93 let mut reader = InstructionReader::new(chunk);
94 let mut ip = reader.ip;
95 let mut span: Option<Span> = None;
96 let mut first = true;
97
98 while let Some(instruction) = reader.next() {
99 let instruction_span = reader
100 .chunk
101 .debug_info
102 .get_source_span(ip as u32)
103 .expect("Missing source span");
104
105 let print_source_lines = if let Some(span) = span {
106 instruction_span.start.line != span.start.line
107 } else {
108 true
109 };
110
111 if print_source_lines && !source_lines.is_empty() {
112 if !first {
113 result += "\n";
114 }
115 first = false;
116
117 let line = instruction_span
118 .start
119 .line
120 .clamp(0, source_lines.len() as u32 - 1) as usize;
121 writeln!(result, "|{}| {}", line + 1, source_lines[line]).ok();
122 span = Some(instruction_span);
123 }
124
125 for (i, line) in format!("{instruction:?}").lines().enumerate() {
126 if i == 0 {
127 writeln!(result, "{ip:<ip_width$}{line}").ok();
128 } else {
129 writeln!(result, "{:ip_width$}{line}", "").ok();
130 }
131 }
132
133 ip = reader.ip;
134 }
135
136 result
137 }
138}
139
140impl fmt::Debug for Chunk {
141 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
142 write!(f, "Chunk ({self:p})")
143 }
144}