/*
Copyright (C) 2019, 2020 Leo Tenenbaum.
This file is part of toc. toc is distributed under version 3 of the GNU General Public License, without any warranty whatsoever.
You should have received a copy of the GNU General Public License along with toc. If not, see .
*/
/* #define ERR_EMACS 1 */
#ifndef USE_COLORED_TEXT
#if ERR_EMACS
#define USE_COLORED_TEXT 0
#else
#define USE_COLORED_TEXT 1
#endif
#endif
#define TEXT_ERR_START "\x1b[91m"
#define TEXT_ERR_END "\x1b[0m"
#define TEXT_INDICATOR_START "\x1b[96m"
#define TEXT_INDICATOR_END "\x1b[0m"
#define TEXT_INFO_START "\x1b[94m"
#define TEXT_INFO_END "\x1b[0m"
#define TEXT_WARN_START "\x1b[93m"
#define TEXT_WARN_END "\x1b[0m"
#define TEXT_IMPORTANT_START "\x1b[1m"
#define TEXT_IMPORTANT_END "\x1b[0m"
#if defined(TOC_DEBUG) && __STDC_VERSION__ >= 199901
#define ERR_SHOW_SOURCE_LOCATION 1
#endif
static inline const char *ordinals(size_t x) {
switch (x % 10) {
case 1: return "st";
case 2: return "nd";
case 3: return "rd";
default: return "th";
}
}
/* Write directly to the error file */
static void err_fwrite(const void *data, size_t size, size_t n) {
fwrite(data, size, n, stderr);
}
static void err_fprint(const char *fmt, ...) {
va_list args;
va_start(args, fmt);
vfprintf(stderr, fmt, args);
va_end(args);
}
static void err_text_err(ErrCtx *ctx, const char *s) {
if (ctx->color_enabled)
err_fprint(TEXT_ERR_START "%s" TEXT_ERR_END, s);
else
err_fprint("%s", s);
}
static void err_text_warn(ErrCtx *ctx, const char *s) {
if (ctx->color_enabled)
err_fprint(TEXT_WARN_START "%s" TEXT_WARN_END, s);
else
err_fprint("%s", s);
}
static void err_text_info(ErrCtx *ctx, const char *s) {
if (ctx->color_enabled)
err_fprint(TEXT_INFO_START "%s" TEXT_INFO_END, s);
else
err_fprint("%s", s);
}
static void err_text_important(ErrCtx *ctx, const char *s) {
if (ctx->color_enabled)
err_fprint(TEXT_IMPORTANT_START "%s" TEXT_IMPORTANT_END, s);
else
err_fprint("%s", s);
}
static void err_vfprint(const char *fmt, va_list args) {
vfprintf(stderr, fmt, args);
}
static void err_print_header_(Location where) {
SourcePos start_pos = where.start->pos;
ErrCtx *ctx = start_pos.ctx;
err_text_err(ctx, "error");
if (!where.start)
err_fprint(":\n");
else {
err_fprint(" at line %lu of %s:\n", (unsigned long)start_pos.line, ctx->filename);
}
}
static void info_print_header_(Location where) {
SourcePos start_pos = where.start->pos;
ErrCtx *ctx = start_pos.ctx;
err_text_info(ctx, "info");
if (!where.start)
err_fprint(":\n");
else {
err_fprint(" at line %lu of %s:\n", (unsigned long)start_pos.line, ctx->filename);
}
}
static void warn_print_header_(Location where) {
SourcePos start_pos = where.start->pos;
ErrCtx *ctx = start_pos.ctx;
err_text_warn(ctx, "warning");
if (!where.start)
err_fprint(":\n");
else {
err_fprint(" at line %lu of %s:\n", (unsigned long)start_pos.line, ctx->filename);
}
}
/*
before_pos and after_pos could be things like TEXT_ERR_START or TEXT_ERR_END.
they will not be printed if color output is disabled.
*/
static void err_print_pos_text(ErrCtx *ctx, U32 start_pos, U32 end_pos) {
(void)end_pos; /* TODO */
char *str = ctx->str;
char *start = str + start_pos;
char *line_start = start;
char *end = str + end_pos;
/* go back to last newline / 0 byte */
while (*line_start != '\0' && *line_start != '\n') --line_start;
if (line_start < start) ++line_start;
char *line_end = strchr(start, '\n');
int has_newline = line_end != NULL;
if (!has_newline)
line_end = strchr(start, '\0');
assert(line_end);
err_fprint("\t");
if (!line_start[0])
err_fprint("");
else {
/* write up to start of error */
err_fwrite(line_start, 1, (size_t)(start - line_start));
if (ctx->color_enabled)
err_fprint(TEXT_INDICATOR_START);
if (line_end < end) {
/* write error part (only go to end of line) */
err_fwrite(start, 1, (size_t)(line_end - start));
if (ctx->color_enabled)
err_fprint(TEXT_INDICATOR_END);
} else {
/* write error part */
err_fwrite(start, 1, (size_t)(end - start));
if (ctx->color_enabled)
err_fprint(TEXT_INDICATOR_END);
/* write rest of line */
err_fwrite(end, 1, (size_t)(line_end - end));
}
}
err_fprint("\n");
}
static void err_print_location_text(Location where) {
if (where.start) {
ErrCtx *ctx = where.start->pos.ctx;
err_print_pos_text(ctx, where.start->pos.start, where.end[-1].pos.end);
} else {
err_fprint("\t");
}
}
static void err_print_footer_(Location where) {
ErrCtx *ctx = where.start->pos.ctx;
err_fprint("\n");
err_print_location_text(where);
if (ctx) {
arr_foreach(ctx->instance_stack, Location, inst) {
err_fprint("While generating the instance of a function\n");
err_print_location_text(*inst);
}
}
}
/* Write nicely-formatted errors to the error file */
static void err_vprint(Location where, const char *fmt, va_list args) {
if (location_is_ctx_disabled(where)) return;
err_print_header_(where);
err_vfprint(fmt, args);
err_print_footer_(where);
}
static void err_print_(
#if ERR_SHOW_SOURCE_LOCATION
int line, const char *file,
#endif
Location where, const char *fmt, ...) {
va_list args;
#if ERR_SHOW_SOURCE_LOCATION
if (location_is_ctx_disabled(where)) return;
if (file)
err_fprint("Generated by line %d of %s:\n", line, file);
#endif
va_start(args, fmt);
err_vprint(where, fmt, args);
va_end(args);
}
#if ERR_SHOW_SOURCE_LOCATION
#define err_print(...) err_print_(__LINE__, __FILE__, __VA_ARGS__)
#else
#define err_print err_print_
#endif
static void info_print(Location where, const char *fmt, ...) {
va_list args;
if (location_is_ctx_disabled(where)) return;
va_start(args, fmt);
info_print_header_(where);
err_vfprint(fmt, args);
err_print_footer_(where);
va_end(args);
}
static void warn_print_(
#if ERR_SHOW_SOURCE_LOCATION
int line, const char *file,
#endif
Location where, const char *fmt, ...) {
va_list args;
if (location_is_ctx_disabled(where)) return;
#if ERR_SHOW_SOURCE_LOCATION
if (file)
err_fprint("Generated by line %d of %s:\n", line, file);
#endif
va_start(args, fmt);
warn_print_header_(where);
err_vfprint(fmt, args);
err_print_footer_(where);
va_end(args);
}
#if ERR_SHOW_SOURCE_LOCATION
#define warn_print(...) warn_print_(__LINE__, __FILE__, __VA_ARGS__)
#else
#define warn_print warn_print_
#endif
static void *err_malloc(size_t size) {
if (size == 0) return NULL;
void *ret = malloc(size);
if (!ret) {
fprintf(stderr, "Error: Out of memory.\n");
abort();
}
return ret;
}
static void *err_calloc(size_t n, size_t size) {
if (n == 0 || size == 0) return NULL;
void *ret = calloc(n, size);
if (!ret) {
fprintf(stderr, "Error: Out of memory.\n");
abort();
}
return ret;
}
static void *err_realloc(void *data, size_t new_size) {
if (new_size == 0) {
free(data);
return NULL;
}
void *ret = realloc(data, new_size);
if (!ret) {
fprintf(stderr, "Error: Out of memory.\n");
abort();
}
return ret;
}