105 lines
2.5 KiB
Odin
105 lines
2.5 KiB
Odin
package source
|
|
|
|
import "core:fmt"
|
|
import "core:mem"
|
|
|
|
Span :: struct {
|
|
start: int,
|
|
end: int,
|
|
}
|
|
|
|
Source :: struct {
|
|
path: string,
|
|
text: string,
|
|
}
|
|
|
|
Diagnostic :: struct {
|
|
span: Span,
|
|
message: string,
|
|
}
|
|
|
|
Diagnostics :: struct {
|
|
source: ^Source,
|
|
items: [dynamic]Diagnostic,
|
|
allocator: mem.Allocator,
|
|
}
|
|
|
|
init_diagnostics :: proc(source_file: ^Source, allocator := context.allocator) -> Diagnostics {
|
|
result: Diagnostics
|
|
result.source = source_file
|
|
result.allocator = allocator
|
|
result.items.allocator = allocator
|
|
return result
|
|
}
|
|
|
|
destroy_diagnostics :: proc(diagnostics: ^Diagnostics) {
|
|
for diagnostic in diagnostics.items {
|
|
delete(diagnostic.message, diagnostics.allocator)
|
|
}
|
|
delete(diagnostics.items)
|
|
}
|
|
|
|
add :: proc(diagnostics: ^Diagnostics, span: Span, message: string) -> int {
|
|
for diagnostic, id in diagnostics.items {
|
|
if diagnostic.span == span && diagnostic.message == message {
|
|
return id
|
|
}
|
|
}
|
|
id := len(diagnostics.items)
|
|
cloned := fmt.aprintf("%s", message, allocator=diagnostics.allocator)
|
|
append(&diagnostics.items, Diagnostic{span=span, message=cloned})
|
|
return id
|
|
}
|
|
|
|
addf :: proc(diagnostics: ^Diagnostics, span: Span, format: string, args: ..any) -> int {
|
|
message := fmt.aprintf(format, ..args, allocator=diagnostics.allocator)
|
|
for diagnostic, id in diagnostics.items {
|
|
if diagnostic.span == span && diagnostic.message == message {
|
|
delete(message, diagnostics.allocator)
|
|
return id
|
|
}
|
|
}
|
|
id := len(diagnostics.items)
|
|
append(&diagnostics.items, Diagnostic{span=span, message=message})
|
|
return id
|
|
}
|
|
|
|
line_and_column :: proc(source_file: ^Source, offset: int) -> (line, column: int) {
|
|
line = 1
|
|
column = 1
|
|
limit := min(offset, len(source_file.text))
|
|
for byte_value in transmute([]byte)source_file.text[:limit] {
|
|
if byte_value == '\n' {
|
|
line += 1
|
|
column = 1
|
|
} else {
|
|
column += 1
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
format :: proc(diagnostics: ^Diagnostics, id: int, allocator := context.allocator) -> string {
|
|
if id < 0 || id >= len(diagnostics.items) {
|
|
return fmt.aprintf("%s: compiler recovery error", diagnostics.source.path, allocator=allocator)
|
|
}
|
|
diagnostic := diagnostics.items[id]
|
|
line, column := line_and_column(diagnostics.source, diagnostic.span.start)
|
|
return fmt.aprintf(
|
|
"%s:%d:%d: error: %s",
|
|
diagnostics.source.path,
|
|
line,
|
|
column,
|
|
diagnostic.message,
|
|
allocator=allocator,
|
|
)
|
|
}
|
|
|
|
print_all :: proc(diagnostics: ^Diagnostics) {
|
|
for _, id in diagnostics.items {
|
|
message := format(diagnostics, id)
|
|
fmt.eprintln(message)
|
|
delete(message)
|
|
}
|
|
}
|