diff options
-rw-r--r-- | src/cli/main.c | 25 | ||||
-rw-r--r-- | src/core/config.h | 15 | ||||
-rw-r--r-- | src/core/state.c | 69 | ||||
-rw-r--r-- | src/core/state.h | 9 | ||||
-rw-r--r-- | src/core/utils.h | 13 |
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 |