summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLinnnus <[email protected]>2025-04-11 23:42:02 +0000
committerLinnnus <[email protected]>2025-04-15 01:08:23 +0000
commitd15dd976b98b4c39a899b3d68047dc752699dca7 (patch)
tree2291c13817e0359d2f116e76e7b66c9ae8f0c590
parent31eacdeefe6099e68dd8596faab8108671e4c6a5 (diff)
feat(core,cli): Add diagnostic handler to SandConfig
-rw-r--r--src/cli/main.c25
-rw-r--r--src/core/config.h15
-rw-r--r--src/core/state.c69
-rw-r--r--src/core/state.h9
-rw-r--r--src/core/utils.h13
5 files changed, 125 insertions, 6 deletions
diff --git a/src/cli/main.c b/src/cli/main.c
index db42a14..cbecf50 100644
--- a/src/cli/main.c
+++ b/src/cli/main.c
@@ -140,15 +140,38 @@ static void print_help(FILE *stream, const char *argv0) {
}
static void print_handler(const char *message, size_t message_length) {
- fprintf(stderr, message, message_length);
+ fprintf(stdout, message, message_length);
+}
+
+void diagnostic_handler(const SandLocation *location, SandDiagnosticLevel level, const char *message, size_t message_length) {
+ sand_print_location(stderr, location);
+ switch (level) {
+ case SAND_DIAGNOSTIC_INFO: fprintf(stderr, ": info"); break;
+ case SAND_DIAGNOSTIC_WARNING: fprintf(stderr, ": warning"); break;
+ case SAND_DIAGNOSTIC_ERROR: fprintf(stderr, ": error"); break;
+ }
+ fprintf(stderr, ": %.*s\n", (int)message_length, message);
}
int main(int argc, char *argv[]) {
SandConfig config = {
.print_handler = print_handler,
+ .diagnostic_handler = diagnostic_handler,
};
SandState state = sand_create_state(config);
+ // TEMP
+ SandLocation loc = {
+ .filename = "fake.sand",
+ .start_line = 0,
+ .start_column = 0,
+ .end_line = 0,
+ .end_column = 5,
+ };
+ sand_print_diagnostic(&state, &loc, SAND_DIAGNOSTIC_INFO, "This is a test info message");
+ sand_print_diagnostic(&state, &loc, SAND_DIAGNOSTIC_WARNING, "oh no the number is %d", 5);
+ sand_print_diagnostic(&state, &loc, SAND_DIAGNOSTIC_ERROR, "gaide gad error here %d", 5);
+
if (argc == 1) {
repl(&state);
} else if (argc == 2 && strcmp(argv[1], "--help") == 0) {
diff --git a/src/core/config.h b/src/core/config.h
index 389a146..63b131b 100644
--- a/src/core/config.h
+++ b/src/core/config.h
@@ -5,11 +5,26 @@
// not opaque) type which the consumer of the core library is supposed to
// assemble themselves. It contains hooks which the State will use for all effects.
+#include "location.h"
+
+#include <stddef.h>
+
// This handler is used for output from the user-supplied actual program.
typedef void (* SandPrintHandler)(const char *message, size_t length);
+typedef enum {
+ SAND_DIAGNOSTIC_INFO,
+ SAND_DIAGNOSTIC_WARNING,
+ SAND_DIAGNOSTIC_ERROR,
+} SandDiagnosticLevel;
+
+// This handler is used when the implementation has a report to make.
+// These could for example be displayed in tan editor's interface.
+typedef void (* SandDiagnosticHandler)(const SandLocation *, SandDiagnosticLevel, const char *message, size_t message_length);
+
typedef struct {
SandPrintHandler print_handler;
+ SandDiagnosticHandler diagnostic_handler;
} SandConfig;
#endif
diff --git a/src/core/state.c b/src/core/state.c
index 91bd9e4..18448eb 100644
--- a/src/core/state.c
+++ b/src/core/state.c
@@ -1,9 +1,72 @@
#include "state.h"
+#include "std_allocator.h"
+
+#include <assert.h>
+#include <stdarg.h>
SandState sand_create_state(SandConfig config) {
- return (SandState) {
- .config = config,
- };
+ SandState S;
+
+ S.config = config;
+ S.gpa = sand_get_std_allocator();
+
+ return S;
+}
+
+char *sand_vasprintf(SandAllocator *a, size_t *length_out, const char *fmt, va_list ap) {
+ va_list ap_clone;
+
+ va_copy(ap_clone, ap);
+ int length = vsnprintf(NULL, 0, fmt, ap_clone);
+ va_end(ap_clone);
+ if (length < 0) {
+ goto fail;
+ }
+
+ char *buffer = sand_allocate(a, length + 1); // Include NUL character.
+ if (buffer == NULL) {
+ goto fail;
+ }
+
+ va_copy(ap_clone, ap);
+ int final_length = vsnprintf(buffer, length + 1, fmt, ap_clone);
+ va_end(ap_clone);
+ assert(final_length == length);
+
+ if (length_out != NULL) {
+ *length_out = length;
+ }
+ return buffer;
+
+fail:
+ if (length_out != NULL) {
+ *length_out = 0;
+ }
+ return NULL;
+}
+
+_SAND_FORMAT_ATTR(printf, 3, 4)
+char *sand_asprintf(SandAllocator *a, size_t *length_out, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ char *result = sand_vasprintf(a, length_out, fmt, ap);
+ va_end(ap);
+ return result;
+}
+
+void sand_print_diagnostic(SandState *S, const SandLocation *location, SandDiagnosticLevel level, const char *fmt, ...) {
+ va_list ap;
+ va_start(ap, fmt);
+ size_t message_length;
+ char *message = sand_vasprintf(&S->gpa, &message_length, fmt, ap);
+ va_end(ap);
+
+ if (message == NULL) {
+ return; // FIXME: What do we do here?
+ }
+
+ S->config.diagnostic_handler(location ,level, message, message_length);
+ sand_deallocate(&S->gpa, message, message_length + 1);
}
void sand_destroy_state(SandState *S) {
diff --git a/src/core/state.h b/src/core/state.h
index 85c604b..6f4d174 100644
--- a/src/core/state.h
+++ b/src/core/state.h
@@ -4,17 +4,22 @@
// This module defines the evaluator state. This is the "world" in Sand.
// Multiple evaluator states can coexist as they are totally separate.
+#include "utils.h"
+#include "allocator.h"
#include "config.h"
-#include <stddef.h>
-
// This data structure should be treated as entirely opaque by consuming code.
typedef struct {
SandConfig config;
+
+ SandAllocator gpa;
} SandState;
SandState sand_create_state(SandConfig config);
+_SAND_FORMAT_ATTR(printf, 4, 5)
+void sand_print_diagnostic(SandState *, const SandLocation *, SandDiagnosticLevel, const char *fmt, ...);
+
void sand_destroy_state(SandState *);
#endif
diff --git a/src/core/utils.h b/src/core/utils.h
new file mode 100644
index 0000000..abb1665
--- /dev/null
+++ b/src/core/utils.h
@@ -0,0 +1,13 @@
+#ifndef SAND_COMPILER_ATTRIBUTES_H
+#define SAND_COMPILER_ATTRIBUTES_H
+
+// This is a helper module which defines various small helpers. It should only
+// be used internally, hence the underscore prefix.
+
+#ifdef __GNUC__
+#define _SAND_FORMAT_ATTR(archetype, string_index, first_to_check) __attribute__((format(archetype, string_index, first_to_check)))
+#else
+#define _SAND_FORMAT_ATTR(archetype, string_index, first_to_check)
+#endif
+
+#endif