jsfw/ser/source.c
viandoxdev eec4668856
ser
2023-05-06 18:42:50 +02:00

298 lines
8.7 KiB
C

#include "source.h"
#include "assert.h"
#include "vector.h"
#include <math.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
uint32_t sss_hash(Hasher state, const void *v) {
SpannedStringSlice *sss = (SpannedStringSlice *)v;
return string_slice_hash(state, &sss->slice);
}
bool sss_equal(const void *a, const void *b) {
SpannedStringSlice *sa = (SpannedStringSlice *)a;
SpannedStringSlice *sb = (SpannedStringSlice *)b;
return string_slice_equal(sa, sb);
}
Source source_init(const char *str, uint32_t len) {
char *ptr = malloc(len + 1);
assert_alloc(ptr);
strncpy(ptr, str, len);
ptr[len] = '\0';
// Will initlalize ref_count to 0 in DEBUG mode as well
return (Source){.str = ptr, .len = len, .path = NULL};
}
SourceError source_from_file(FILE *f, Source *src) {
fseek(f, 0, SEEK_END);
uint64_t len = ftell(f);
fseek(f, 0, SEEK_SET);
char *ptr = malloc(len + 1);
if (fread(ptr, 1, len, f) != len) {
return SourceErrorReadFailed;
}
IF_DEBUG(src->ref_count = 0);
src->str = ptr;
src->len = len;
src->path = NULL;
return SourceErrorNoError;
}
SourceError source_open(const char *path, Source *src) {
FILE *f = fopen(path, "r");
if (f == NULL) {
return SourceErrorOpenFailed;
}
SourceError err = source_from_file(f, src);
fclose(f);
if (err == SourceErrorNoError) {
char *p = strdup(path);
assert_alloc(p);
src->path = p;
}
return err;
}
void source_drop(Source src) {
IF_DEBUG({
if (src.ref_count > 0) {
log_error("Trying to destroy currently used source, leaking instead");
return;
}
});
if (src.path != NULL) {
free((char *)src.path);
}
free((char *)src.str);
}
int span_compare(const void *sa, const void *sb) {
Span *a = (Span *)sa;
Span *b = (Span *)sb;
int line = a->loc.line - b->loc.line;
if (line != 0)
return line;
int column = b->loc.column - a->loc.column;
if (column != 0)
return column;
return a->len - b->len;
}
static int report_span_compare(const void *va, const void *vb) {
ReportSpan *a = (ReportSpan *)va;
ReportSpan *b = (ReportSpan *)vb;
return span_compare(&a->span, &b->span);
}
void source_report(
const Source *src,
Location loc,
ReportSeverity sev,
const ReportSpan *pspans,
uint32_t span_count,
const char *help,
const char *fmt,
...
) {
va_list args;
va_start(args, fmt);
int len = vsnprintf(NULL, 0, fmt, args);
va_end(args);
char *message = malloc(len + 1);
assert_alloc(message);
va_start(args, fmt);
vsnprintf(message, len + 1, fmt, args);
va_end(args);
ReportSpanVec spans = vec_init();
vec_push_array(&spans, pspans, span_count);
qsort(spans.data, spans.len, sizeof(ReportSpan), report_span_compare);
const char *s;
switch (sev) {
case ReportSeverityError:
s = "\033[91merror";
break;
case ReportSeverityWarning:
s = "\033[93mwarning";
break;
case ReportSeverityNote:
s = "\033[92mnote";
break;
default:
s = "?????";
break;
}
const char *file;
if (src->path == NULL) {
file = "<unknown source>";
} else {
file = src->path;
}
uint32_t last_line, first_line;
if (spans.len > 0) {
last_line = spans.data[spans.len - 1].span.loc.line;
first_line = spans.data[0].span.loc.line;
} else {
last_line = loc.line;
first_line = loc.line;
}
uint32_t pad = floor(log10(last_line)) + 2;
fprintf(
stderr,
"\033[1m%s\033[0;1m: %s\n%*s\033[94m--> \033[0m%s:%d:%d\n%*s\033[1;94m|\n",
s,
message,
pad - 1,
"",
file,
loc.line,
loc.column,
pad,
""
);
free(message);
// The line of the span
StyledString line_str = styled_string_init();
// Extra lines used when no more space in the sub
StyledStringVec sub_strs = vec_init();
uint32_t line_length;
uint32_t offset;
last_line = first_line - 1;
for (uint32_t i = 0; i < spans.len; i++) {
ReportSpan rspan = spans.data[i];
Span span = rspan.span;
offset = span.loc.offset - span.loc.column;
uint32_t line_end_off = offset;
while (line_end_off < src->len && src->str[line_end_off] != '\n') {
line_end_off++;
}
uint32_t line_delta = span.loc.line - last_line;
line_length = line_end_off - offset;
last_line = span.loc.line;
styled_string_clear(&line_str);
vec_clear(&sub_strs);
vec_push(&sub_strs, styled_string_init());
styled_string_set(&line_str, 0, NULL, &src->str[offset], line_length);
while (i < spans.len && spans.data[i].span.loc.line == last_line) {
ReportSpan rspan = spans.data[i];
Span span = rspan.span;
ReportSeverity span_sev = rspan.sev;
const char *sev_style = "\033[1;97m";
char underline = ' ';
switch (span_sev) {
case ReportSeverityError:
sev_style = "\033[1;91m";
underline = '^';
break;
case ReportSeverityWarning:
sev_style = "\033[1;93m";
underline = '^';
break;
case ReportSeverityNote:
sev_style = "\033[1;94m";
underline = '-';
break;
}
styled_string_set_style(&line_str, span.loc.column, sev_style, span.len);
styled_string_set_style(sub_strs.data, span.loc.column, sev_style, span.len);
styled_string_fill(&sub_strs.data[0], span.loc.column, underline, span.len);
// Not a loop, but I want break;
while (rspan.message != NULL) {
int mlen = strlen(rspan.message);
size_t index = span.loc.column + span.len + 1;
if (styled_string_available_space(&sub_strs.data[0], index, mlen + 1) > mlen) {
styled_string_set(&sub_strs.data[0], index, sev_style, rspan.message, mlen);
// We got the message in
break;
}
index = span.loc.column;
// We never put any message on the second sub string, so it needs to exist if we put one on the third
if (sub_strs.len == 1) {
vec_push(&sub_strs, styled_string_init());
}
// Start looking at the third sub line
size_t line = 2;
while (true) {
// The line doesn't exist yet: it is available
if (line >= sub_strs.len) {
vec_push(&sub_strs, styled_string_init());
break;
}
// Check if the subline is ok
if (styled_string_available_space(&sub_strs.data[line], index, mlen + 1) > mlen) {
break;
}
// We couldn't find any space, continue on the next line.
line++;
}
for (size_t l = 1; l < line; l++) {
styled_string_set(&sub_strs.data[l], index, sev_style, "|", 1);
}
styled_string_set(&sub_strs.data[line], index, sev_style, rspan.message, mlen);
break;
}
i++;
}
// We exited the loop, i points to a span on the next line or to the end of spans
// Se decrement it because it'll get reincremented by the outer for loop
i--;
// Print elipsies if we skipped more than a line
if (line_delta > 2) {
fprintf(stderr, "\033[1;94m...\n");
} else if (line_delta > 1) {
uint32_t off_end = offset - 1;
uint32_t off_start = off_end;
while (src->str[off_start - 1] != '\n' && off_start > 0) {
off_start--;
}
uint32_t len = off_end - off_start;
fprintf(stderr, "\033[1;94m%*d |\033[0m %.*s\n", pad - 1, last_line - 1, len, &src->str[off_start]);
}
char *line = styled_string_build(&line_str);
fprintf(stderr, "\033[1;94m%*d |\033[0m %s\n", pad - 1, last_line, line);
free(line);
for (size_t i = 0; i < sub_strs.len; i++) {
line = styled_string_build(&sub_strs.data[i]);
fprintf(stderr, "%*s\033[1;94m|\033[0m %s\n", pad, "", line);
free(line);
}
}
styled_string_drop(line_str);
vec_drop(sub_strs);
vec_drop(spans);
if (help != NULL) {
fprintf(stderr, "\033[1;96mhelp\033[0m: %s\n", help);
}
}