///|
pub struct StackFrame {
function_name : String?
file : String
line : Int
column : Int
} derive(Eq, Debug, ToJson)
///|
pub struct SymbolicatedFrame {
generated : StackFrame
original : OriginalPosition?
} derive(Eq, Debug, ToJson)
///|
fn parse_decimal(text : StringView) -> Int? {
if text.length() == 0 {
return None
}
let mut value = 0
for ch in text {
if ch < '0' || ch > '9' {
return None
}
value = value * 10 + (ch.to_int() - '0'.to_int())
}
Some(value)
}
///|
fn strip_wrapping_parens(text : StringView) -> StringView {
let trimmed = text.trim()
if trimmed.has_prefix("(") && trimmed.has_suffix(")") && trimmed.length() >= 2 {
trimmed[1:trimmed.length() - 1]
} else if trimmed.has_suffix(")") && trimmed.length() >= 1 {
trimmed[:trimmed.length() - 1]
} else {
trimmed
}
}
///|
fn parse_location(text : StringView) -> StackFrame? {
let location = strip_wrapping_parens(text)
match location.rev_split_once(":") {
Some((file_and_line, column_text)) =>
match file_and_line.rev_split_once(":") {
Some((file, line_text)) =>
match (parse_decimal(line_text), parse_decimal(column_text)) {
(Some(line), Some(column)) =>
Some({ function_name: None, file: file.to_owned(), line, column })
_ => None
}
None => None
}
None => None
}
}
///|
pub fn parse_stack_frame(line : StringView) -> StackFrame? {
let trimmed = line.trim()
let frame_text = match trimmed.strip_prefix("at ") {
Some(rest) => rest.trim()
None => trimmed
}
match frame_text.split_once(" (") {
Some((name, loc)) =>
match parse_location(loc) {
Some(frame) =>
Some({ ..frame, function_name: Some(name.trim().to_owned()) })
None => None
}
None => parse_location(frame_text)
}
}
///|
pub fn parse_stack(text : StringView) -> Array[StackFrame] {
let frames : Array[StackFrame] = []
for line in text.split("\n") {
match parse_stack_frame(line) {
Some(frame) => frames.push(frame)
None => ()
}
}
frames
}
///|
pub fn SourceMap::symbolicate_frame(
self : SourceMap,
frame : StackFrame,
) -> SymbolicatedFrame {
{ generated: frame, original: self.find_original(frame.line, frame.column) }
}
///|
pub fn SourceMap::symbolicate_stack(
self : SourceMap,
stack : StringView,
) -> Array[SymbolicatedFrame] {
parse_stack(stack).map(frame => self.symbolicate_frame(frame))
}
///|
pub fn StackFrame::render(self : StackFrame) -> String {
match self.function_name {
Some(name) => "at \{name} (\{self.file}:\{self.line}:\{self.column})"
None => "at \{self.file}:\{self.line}:\{self.column}"
}
}
///|
pub fn SymbolicatedFrame::render(self : SymbolicatedFrame) -> String {
match self.original {
Some(original) => {
let name = match original.name {
Some(value) => value
None =>
match self.generated.function_name {
Some(value) => value
None => ""
}
}
"at \{name} (\{original.source}:\{original.line}:\{original.column}) <- \{self.generated.file}:\{self.generated.line}:\{self.generated.column}"
}
None => self.generated.render()
}
}
///|
pub fn render_symbolicated_stack(
frames : ArrayView[SymbolicatedFrame],
) -> String {
frames.map(frame => frame.render()).join("\n")
}
///|
pub fn SourceMap::symbolicate_stack_report(
self : SourceMap,
stack : StringView,
) -> String {
render_symbolicated_stack(self.symbolicate_stack(stack))
}