aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/diag/diag.c63
-rw-r--r--src/diag/diag.h12
-rw-r--r--src/io/file.c18
-rw-r--r--src/lexer/lexer.c1
-rw-r--r--src/lexer/lexer.h1
-rw-r--r--src/main.c11
-rw-r--r--src/parser/parser.c6
-rw-r--r--src/sema/sema.c22
-rw-r--r--src/sema/sema.h3
9 files changed, 118 insertions, 19 deletions
diff --git a/src/diag/diag.c b/src/diag/diag.c
new file mode 100644
index 0000000..cc61e89
--- /dev/null
+++ b/src/diag/diag.c
@@ -0,0 +1,63 @@
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "diag/diag.h"
+
+static bool colored(void)
+{
+ return getenv("NO_COLOR") == NULL;
+}
+
+void report_error(struct Source source, struct Token token, const char* message)
+{
+ const char* name = source.name != NULL ? source.name : "<source>";
+ const char* text = source.text;
+
+ const char* line_start = token.start;
+ while (line_start > text && line_start[-1] != '\n')
+ line_start -= 1;
+
+ const char* line_end = token.start;
+ while (*line_end != '\0' && *line_end != '\n')
+ line_end += 1;
+
+ uint32_t column = (uint32_t)(token.start - line_start) + 1;
+
+ size_t span = token.length > 0 ? token.length : 1;
+ if ((size_t)(line_end - token.start) < span)
+ span = (size_t)(line_end - token.start);
+ if (span == 0)
+ span = 1;
+
+ const char* bold = colored() ? "\033[1m" : "";
+ const char* red = colored() ? "\033[31m" : "";
+ const char* blue = colored() ? "\033[34m" : "";
+ const char* reset = colored() ? "\033[0m" : "";
+
+ int gutter = snprintf(NULL, 0, "%u", token.line);
+
+ fprintf(stderr, "%s%serror:%s %s%s%s\n", bold, red, reset, bold, message, reset);
+ fprintf(stderr, "%*s %s-->%s %s:%u:%u\n", gutter, "", blue, reset, name, token.line, column);
+ fprintf(stderr, "%*s %s|%s\n", gutter, "", blue, reset);
+ fprintf(stderr, "%s%u%s %s|%s %.*s\n", blue, token.line, reset, blue, reset,
+ (int)(line_end - line_start), line_start);
+ fprintf(stderr, "%*s %s|%s ", gutter, "", blue, reset);
+
+ for (const char* character = line_start; character < token.start; character += 1)
+ fputc(*character == '\t' ? '\t' : ' ', stderr);
+
+ fprintf(stderr, "%s", red);
+ for (size_t i = 0; i < span; i += 1)
+ fputc('^', stderr);
+ fprintf(stderr, "%s\n", reset);
+}
+
+void report_error_message(const char* message)
+{
+ const char* bold = colored() ? "\033[1m" : "";
+ const char* red = colored() ? "\033[31m" : "";
+ const char* reset = colored() ? "\033[0m" : "";
+
+ fprintf(stderr, "%s%serror:%s %s%s%s\n", bold, red, reset, bold, message, reset);
+}
diff --git a/src/diag/diag.h b/src/diag/diag.h
new file mode 100644
index 0000000..7a99266
--- /dev/null
+++ b/src/diag/diag.h
@@ -0,0 +1,12 @@
+#pragma once
+
+#include "lexer/lexer.h"
+
+struct Source
+{
+ const char* name;
+ const char* text;
+};
+
+void report_error(struct Source source, struct Token token, const char* message);
+void report_error_message(const char* message);
diff --git a/src/io/file.c b/src/io/file.c
index 8ce8eef..3d7083c 100644
--- a/src/io/file.c
+++ b/src/io/file.c
@@ -1,20 +1,28 @@
#include <stdio.h>
#include <stdlib.h>
+#include "diag/diag.h"
#include "io/file.h"
+static void report_file_error(const char* verb, const char* path)
+{
+ char message[512];
+ snprintf(message, sizeof(message), "%s '%s'", verb, path);
+ report_error_message(message);
+}
+
bool read_file(const char* path, struct File* out)
{
FILE* stream = fopen(path, "rb");
if (stream == NULL)
{
- fprintf(stderr, "error: could not open '%s'\n", path);
+ report_file_error("could not open", path);
return false;
}
if (fseek(stream, 0, SEEK_END) != 0)
{
- fprintf(stderr, "error: could not read '%s'\n", path);
+ report_file_error("could not read", path);
fclose(stream);
return false;
}
@@ -22,7 +30,7 @@ bool read_file(const char* path, struct File* out)
long length = ftell(stream);
if (length < 0)
{
- fprintf(stderr, "error: could not read '%s'\n", path);
+ report_file_error("could not read", path);
fclose(stream);
return false;
}
@@ -32,14 +40,14 @@ bool read_file(const char* path, struct File* out)
char* data = malloc(size + 1);
if (data == NULL)
{
- fprintf(stderr, "error: out of memory reading '%s'\n", path);
+ report_file_error("out of memory reading", path);
fclose(stream);
return false;
}
if (fread(data, 1, size, stream) != size)
{
- fprintf(stderr, "error: could not read '%s'\n", path);
+ report_file_error("could not read", path);
free(data);
fclose(stream);
return false;
diff --git a/src/lexer/lexer.c b/src/lexer/lexer.c
index da45741..fb3fce7 100644
--- a/src/lexer/lexer.c
+++ b/src/lexer/lexer.c
@@ -118,6 +118,7 @@ static struct Token make_token(struct Lexer* lexer, enum TokenType type, const c
struct Lexer create_lexer(const char* source)
{
struct Lexer lexer;
+ lexer.name = NULL;
lexer.source = source;
lexer.current = source;
lexer.line = 1;
diff --git a/src/lexer/lexer.h b/src/lexer/lexer.h
index 441fe10..afe6e7f 100644
--- a/src/lexer/lexer.h
+++ b/src/lexer/lexer.h
@@ -64,6 +64,7 @@ struct Token
struct Lexer
{
+ const char* name;
const char* source;
const char* current;
uint32_t line;
diff --git a/src/main.c b/src/main.c
index 90b3c82..71e8918 100644
--- a/src/main.c
+++ b/src/main.c
@@ -2,6 +2,7 @@
#include "io/file.h"
#include "cli/args.h"
+#include "diag/diag.h"
#include "sema/sema.h"
#include "lexer/lexer.h"
#include "codegen/nasm.h"
@@ -19,7 +20,7 @@ int main(int argc, char** argv)
if (args.target != ASSEMBLER_NASM)
{
- fprintf(stderr, "error: only the nasm target is supported\n");
+ report_error_message("only the nasm target is supported");
return 1;
}
@@ -28,6 +29,7 @@ int main(int argc, char** argv)
return 1;
struct Lexer lexer = create_lexer(source.data);
+ lexer.name = args.input_path;
struct Program program;
if (!parse_program(&lexer, &program))
@@ -37,7 +39,8 @@ int main(int argc, char** argv)
return 1;
}
- if (!analyze_program(&program))
+ struct Source diagnostics = { args.input_path, source.data };
+ if (!analyze_program(diagnostics, &program))
{
free_program(&program);
free_file(&source);
@@ -50,7 +53,9 @@ int main(int argc, char** argv)
out = fopen(args.output_path, "w");
if (out == NULL)
{
- fprintf(stderr, "error: could not open '%s' for writing\n", args.output_path);
+ char message[256];
+ snprintf(message, sizeof(message), "could not open '%s' for writing", args.output_path);
+ report_error_message(message);
free_program(&program);
free_file(&source);
return 1;
diff --git a/src/parser/parser.c b/src/parser/parser.c
index 7ec4474..beb6b04 100644
--- a/src/parser/parser.c
+++ b/src/parser/parser.c
@@ -2,11 +2,13 @@
#include <stdlib.h>
#include <string.h>
+#include "diag/diag.h"
#include "parser/parser.h"
struct Parser
{
struct Lexer* lexer;
+ struct Source source;
struct Token current;
struct Token previous;
bool had_error;
@@ -25,7 +27,7 @@ static bool check(struct Parser* parser, enum TokenType type)
static void error_at(struct Parser* parser, struct Token token, const char* message)
{
- fprintf(stderr, "error: line %u: %s\n", token.line, message);
+ report_error(parser->source, token, message);
parser->had_error = true;
}
@@ -509,6 +511,8 @@ bool parse_program(struct Lexer* lexer, struct Program* out)
{
struct Parser parser;
parser.lexer = lexer;
+ parser.source.name = lexer->name;
+ parser.source.text = lexer->source;
parser.had_error = false;
advance_parser(&parser);
diff --git a/src/sema/sema.c b/src/sema/sema.c
index 55f4a93..f86159c 100644
--- a/src/sema/sema.c
+++ b/src/sema/sema.c
@@ -9,7 +9,7 @@ static bool names_equal(struct Token a, struct Token b)
return a.length == b.length && memcmp(a.start, b.start, a.length) == 0;
}
-static bool check_duplicate_names(struct Program* program)
+static bool check_duplicate_names(struct Source source, struct Program* program)
{
size_t count = program->const_count + program->data_count + program->proc_count;
if (count == 0)
@@ -38,8 +38,10 @@ static bool check_duplicate_names(struct Program* program)
for (size_t j = 0; j < i; j += 1)
if (names_equal(names[i], names[j]))
{
- fprintf(stderr, "error: line %u: '%.*s' is already defined\n",
- names[i].line, (int)names[i].length, names[i].start);
+ char message[128];
+ snprintf(message, sizeof(message), "'%.*s' is already defined",
+ (int)names[i].length, names[i].start);
+ report_error(source, names[i], message);
ok = false;
}
@@ -47,7 +49,7 @@ static bool check_duplicate_names(struct Program* program)
return ok;
}
-static bool check_entry_point(struct Program* program)
+static bool check_entry_point(struct Source source, struct Program* program)
{
if (!program->config.has_entry)
return true;
@@ -57,18 +59,20 @@ static bool check_entry_point(struct Program* program)
if (names_equal(program->procs[i].name, entry))
return true;
- fprintf(stderr, "error: line %u: entry point '%.*s' is not defined\n",
- entry.line, (int)entry.length, entry.start);
+ char message[128];
+ snprintf(message, sizeof(message), "entry point '%.*s' is not defined",
+ (int)entry.length, entry.start);
+ report_error(source, entry, message);
return false;
}
-bool analyze_program(struct Program* program)
+bool analyze_program(struct Source source, struct Program* program)
{
bool ok = true;
- if (!check_duplicate_names(program))
+ if (!check_duplicate_names(source, program))
ok = false;
- if (!check_entry_point(program))
+ if (!check_entry_point(source, program))
ok = false;
return ok;
diff --git a/src/sema/sema.h b/src/sema/sema.h
index e015f41..468d1be 100644
--- a/src/sema/sema.h
+++ b/src/sema/sema.h
@@ -2,6 +2,7 @@
#include <stdbool.h>
+#include "diag/diag.h"
#include "parser/ast.h"
-bool analyze_program(struct Program* program);
+bool analyze_program(struct Source source, struct Program* program);