libpulsar
A modular compiler for the pulsar programming language
Loading...
Searching...
No Matches
error.c
Go to the documentation of this file.
1// Copyright (C) 2023 Ethan Uppal. All rights reserved.
2
3#include <stdio.h>
4#include <stdlib.h>
5#include <ctype.h>
6#include <string.h>
7#include "error.h"
9#include "util/abort.h"
10#ifdef COMPILED_FROM_MAKE
11 #include "termcolor.h"
12#else
13 #include "../libtermcolor/include/termcolor.h"
14#endif
15#include "enum/make.h"
16
17// not sure why this redeclaration from frontend/lexer/loc.h is necessary to
18// make it compile
19bool ps_is_loc_none(const struct ps_loc* loc);
20
21// defines to make stream writing easier.
22#define _PRINTF(...) tcol_fprintf(stream, __VA_ARGS__)
23#define _PRINTF_FST(...) fprintf(stream, __VA_ARGS__)
24// #define _PRINTF(...) (printer->stream, __VA_ARGS__)
25#define _PUTCHR(__c) fputc(__c, stream)
26
27static STR error_scope_strs[] = {
28#define ENUM TO_STRING
29#include "error_scope.h"
30#undef ENUM
31};
32static STR error_scope_color_strs[] = {
33#define ENUM TO_STRING2
34#include "error_scope.h"
35#undef ENUM
36};
37static STR error_code_strs[] = {
38#define ENUM TO_STRING
39#include "error_codes.h"
40#undef ENUM
41};
42
44static usize error_count;
45
47static struct ps_error errors[PS_MAX_ERRORS];
48
49// Produces the ANSI escape sequence for a given termcolor-style string.
50// Inefficient but works.
51static const char* ps_color(const char* tc_color) {
52#define N_COLORS_STORABLE 20
53#define N_ESTIMATED_MAX_COLOR_LENGTH 24
54
55 // store all color strings in a ring buffer
56 static char buffer[N_COLORS_STORABLE * 16];
57
58 // wrap around the ring buffer to avoid writing past its end
59 static usize used = 0;
60 if (used + N_ESTIMATED_MAX_COLOR_LENGTH + 1 > sizeof(buffer)) {
61 used = 0;
62 }
63
64 // parse the termcolor-style color into an ANSI color and write it as a
65 // null-terminated string inot the buffer
67 char* result = buffer + used;
68 if (tcol_color_parse(result, N_ESTIMATED_MAX_COLOR_LENGTH, (char*)tc_color,
69 strlen(tc_color), &length)
70 != TermColorErrorNone) {
71 result = NULL;
72 }
73 used += length + 1;
74 result[length] = 0;
75
76 return result;
77
78#undef N_COLORS_STORABLE
79#undef N_ESTIMATED_MAX_COLOR_LENGTH
80}
81
82void ps_error(enum ps_error_scope scope, short code, const char* file_source,
83 struct ps_loc loc, usize length, const char* message, const char* explain,
84 const char* fix) {
85 // we only add errors if there is space
86 if (error_count + 1 < PS_MAX_ERRORS) {
87 // construct the error and add it to the error buffer
88 struct ps_error* err = &errors[error_count++];
89 err->scope = scope;
90 err->code = code;
92 err->loc = loc;
93 err->length = file_source[err->loc.pos] == '\n' ? 0 : length;
94 err->message = message;
95 err->explain = explain;
96 err->fix = fix;
97 }
98}
99
102static inline void ps_fprint_error_primary(FILE* stream, struct ps_error* err) {
103 if (!ps_is_loc_none(&err->loc)) {
104 // write the error location
105 _PRINTF("{+}%s{0}:%zu:%zu: ", err->loc.filename, err->loc.line,
106 err->loc.col);
107 }
108 // write the error domain
109 _PRINTF_FST("%s%s", err->theme, error_scope_strs[err->scope]);
110 // write the error code
111 if (err->code >= 0) {
112 _PRINTF_FST("[%c%04d]", toupper(error_scope_strs[err->scope][0]),
113 err->code);
114 }
115
116 // write the main message
117 _PRINTF("{0}: %s\n", err->message);
118}
119
122static inline void ps_fprintf_error_line_prefix(FILE* stream,
123 struct ps_error* err, isize line) {
124 if (line == -1) {
125 _PRINTF(" %s┃{0} ", err->theme);
126 } else {
127 _PRINTF(" %4zu %s┃{0} ", (usize)line, err->theme);
128 }
129}
130
132static inline void ps_fprintf_error_explain(FILE* stream, struct ps_error* err,
133 usize chars_before_error) {
134 // we draw a smooth line underneath the region of error
135 ps_fprintf_error_line_prefix(stream, err, -1);
136 _PRINTF_FST("%*s%s%s", (int)chars_before_error, "", err->theme,
137 err->length > 1 ? "┌" : "│");
138 for (usize i = 1; i < err->length; i++) {
139 _PRINTF_FST("─");
140 }
141 _PUTCHR('\n');
142
143 // the explain message is drawn underneath this region-outlining line
144 ps_fprintf_error_line_prefix(stream, err, -1);
145 _PRINTF_FST("%*s%s│\n", (int)chars_before_error, "", err->theme);
146 ps_fprintf_error_line_prefix(stream, err, -1);
147 _PRINTF("%*s%s{0}{/}%s{0}\n", (int)chars_before_error, "", err->theme,
148 err->explain);
149}
150
152static inline void ps_fprintf_error_fix(FILE* stream, struct ps_error* err) {
153 // write it on a new line
154 _PRINTF_FST("%s\n", err->fix);
155}
156
158static inline void ps_fprintf_error_line(FILE* stream, struct ps_error* err) {
159 ps_fprintf_error_line_prefix(stream, err, err->loc.line);
160
161 // find line start
162 usize line_start = err->loc.pos;
163 if (line_start > 0 && err->file_source[line_start] == '\n') {
164 line_start--;
165 }
166 while (line_start > 0 && err->file_source[line_start] != '\n') {
167 line_start--;
168 }
169 if (err->loc.line > 1 /* the first line is not prefixed with a newline */) {
170 line_start++;
171 }
172
173 // determine the length of the line
174 usize line_length = 0;
175 char end_char;
176 while ((end_char = err->file_source[line_start + line_length]),
177 end_char != '\n' && end_char != 0) {
178 line_length++;
179 }
180
181 // find the ranges of the line start and the error region
182 usize chars_before_error = err->loc.pos - line_start;
183 usize chars_after_error = line_length - (chars_before_error + err->length);
184 usize start_after_error = line_start + chars_before_error + err->length;
185
186 // display the line
187 _PRINTF(
188 "%.*s"
189 "%s%.*s{0}"
190 "%.*s\n",
191 (int)chars_before_error, &err->file_source[line_start],
192
193 err->theme, (int)err->length,
194 &err->file_source[line_start + chars_before_error],
195
196 (int)chars_after_error, &err->file_source[start_after_error]);
197
198 // if additional messages are available, display them
199 if (err->explain) {
200 ps_fprintf_error_explain(stream, err, chars_before_error);
201 }
202 if (err->fix) {
203 ps_fprintf_error_fix(stream, err);
204 }
205}
206
208void ps_fprint_error(FILE* stream, struct ps_error* err) {
209 ps_fprint_error_primary(stream, err);
210 if (!ps_is_loc_none(&err->loc)) {
211 ps_fprintf_error_line(stream, err);
212 }
213}
214
215void ps_fprint_errors(FILE* stream) {
216 // respect the choices and system requirements for whether colored output is
217 // given
218 tcol_auto_color(stream);
219
220 // print all the errors to stream
221 for (usize i = 0; i < error_count; i++) {
222 // if (i > 0) {
223 // _PUTCHR('\n');
224 // }
225
226 struct ps_error* error = &errors[i];
227
228 // not great
229 error->theme = ps_color(error_scope_color_strs[error->scope]);
230 error->theme = tcol_dyn(error->theme);
231
232 ps_fprint_error(stream, error);
233 }
234
235 // every error we printed is now "consumed," so we start afresh
236 error_count = 0;
237}
238
239// getters are eh but whatever
241 return error_count;
242}
Defines assertion and abortion functionality.
#define STR
Definition def.h:40
#define isize
Definition def.h:49
#define usize
Definition def.h:50
#define N_ESTIMATED_MAX_COLOR_LENGTH
usize ps_error_count(void)
The number of errors that have been reported in the error reporting system.
Definition error.c:240
#define N_COLORS_STORABLE
#define _PUTCHR(__c)
Definition error.c:25
#define _PRINTF_FST(...)
Definition error.c:23
void ps_fprint_errors(FILE *stream)
Prints all errors in the error-reporting system to the given output stream stream.
Definition error.c:215
#define _PRINTF(...)
Definition error.c:22
void ps_fprint_error(FILE *stream, struct ps_error *err)
Displays the err on the given output stream stream.
Definition error.c:208
bool ps_is_loc_none(const struct ps_loc *loc)
Definition loc.c:20
Error reporting and displaying utilities.
#define PS_MAX_ERRORS
Definition error.h:17
ps_error_scope
THe domain of error that occured.
Definition error.h:23
Error codes.
Error scopes.
char buffer[ITOA_BUFFER_SIZE]
Definition itoa.c:11
static void usize struct ps_loc loc
Definition lexer.h:76
static void usize length
Definition lexer.h:71
File location structure.
Retrieval of associated data from custom enum (see enum.h).
Represents an error or source-referencing display message.
Definition error.h:43
struct ps_loc loc
Definition error.h:49
const char * explain
Definition error.h:53
short code
Definition error.h:45
const char * fix
Definition error.h:54
const char * file_source
Definition error.h:47
const char * theme
Definition error.h:56
enum ps_error_scope scope
Definition error.h:44
const char * message
Definition error.h:52
usize length
Definition error.h:50
Represents a token location.
Definition loc.h:16
STR filename
The file where the token is from.
Definition loc.h:17
usize pos
The token's position in the text.
Definition loc.h:20
usize line
The line number where the token is found.
Definition loc.h:18
usize col
The column number where the token starts.
Definition loc.h:19