From 146aa4d9d13ec29ffa8aa33868c41d80a9a2fbce Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Wed, 29 Apr 2026 10:20:30 +0200 Subject: [PATCH] Convert codebase to C89 compatibility and update test scripts --- v0/ast.h | 22 ++++----- v0/bool.h | 10 +++++ v0/include.mk | 12 ++++- v0/location.h | 18 ++++---- v0/log.c | 36 ++++++++------- v0/main.c | 5 ++- v0/parser.c | 70 ++++++++++++++++------------- v0/test.c | 44 ++++++++++++------ v0/test.h | 4 +- v0/test_log.c | 30 ++++++------- v0/test_token.c | 48 ++++++++++++-------- v0/token.c | 115 +++++++++++++++++++++++++++--------------------- v0/token.h | 36 +++++++-------- v0/util.c | 29 +++++++++--- 14 files changed, 287 insertions(+), 192 deletions(-) create mode 100644 v0/bool.h diff --git a/v0/ast.h b/v0/ast.h index e1990d5..348713f 100644 --- a/v0/ast.h +++ b/v0/ast.h @@ -4,15 +4,15 @@ #ifndef AST_H #define AST_H -#include +#include "bool.h" #include typedef struct { - /// @brief The name of the module being imported. - char* module_name; + /* @brief The name of the module being imported. */ + char* module_name; - /// @brief Whether the import is public or not. - bool is_public; + /* @brief Whether the import is public or not. */ + bool is_public; } ImportDeclaration; /** @@ -20,14 +20,14 @@ typedef struct { * Every file matches an entire Module. */ typedef struct { - /// @brief The name of the module. - char* name; + /* @brief The name of the module. */ + char* name; - /// @brief The list of imports in the module. - ImportDeclaration* imports; + /* @brief The list of imports in the module. */ + ImportDeclaration* imports; - /// @brief The number of imports in the module. - size_t import_count; + /* @brief The number of imports in the module. */ + size_t import_count; } Module; #endif diff --git a/v0/bool.h b/v0/bool.h new file mode 100644 index 0000000..ca3bde7 --- /dev/null +++ b/v0/bool.h @@ -0,0 +1,10 @@ +/* Minimal boolean type for C89 compatibility */ +#ifndef BOOL_H +#define BOOL_H + +typedef int bool; + +#define true 1 +#define false 0 + +#endif diff --git a/v0/include.mk b/v0/include.mk index b81c728..766f1a6 100644 --- a/v0/include.mk +++ b/v0/include.mk @@ -11,6 +11,8 @@ V0_TEST_OBJ := $(patsubst v0/%.c,v0/bin/%.o,$(V0_TEST)) V0_SRC_DEPS := $(V0_SRC_OBJ:.o=.d) V0_TEST_DEPS := $(V0_TEST_OBJ:.o=.d) +CFLAGS += -Werror -Wall -pedantic -std=c89 + v0/bin/c2: $(V0_SRC_OBJ) $(CC) $(CFLAGS) -o $@ $^ @@ -19,8 +21,16 @@ V0_SRC_OBJ_NO_MAIN := $(filter-out v0/bin/main.o,$(V0_SRC_OBJ)) v0/bin/test: $(V0_SRC_OBJ_NO_MAIN) $(V0_TEST_OBJ) $(CC) $(CFLAGS) -o $@ $^ +# Only run tests under valgrind on Linux. On macOS (Darwin) valgrind is +# typically unavailable or unsupported, so run the test binary directly. +ifeq ($(shell uname -s),Linux) +TEST_CMD := valgrind --quiet --leak-check=full --error-exitcode=1 v0/bin/test +else +TEST_CMD := v0/bin/test +endif + test:: v0/bin/test - valgrind --quiet --leak-check=full --error-exitcode=1 v0/bin/test + $(TEST_CMD) generate_golden:: v0/bin/test GENERATE_GOLDEN=1 v0/bin/test diff --git a/v0/location.h b/v0/location.h index 21d93c2..abd2c48 100644 --- a/v0/location.h +++ b/v0/location.h @@ -9,20 +9,20 @@ #include typedef struct { - /// @brief The name of the file where the token was found. + /* @brief The name of the file where the token was found. */ char* filename; - /// @brief The entire line of text where the token was found. - String line_text; + /* @brief The entire line of text where the token was found. */ + String line_text; - /// @brief The line number where the token was found. - int line; + /* @brief The line number where the token was found. */ + int line; - /// @brief The starting column number where the token was found. - int column_start; + /* @brief The starting column number where the token was found. */ + int column_start; - /// @brief The ending column number where the token was found. - int column_end; + /* @brief The ending column number where the token was found. */ + int column_end; } Location; #endif diff --git a/v0/log.c b/v0/log.c index 4a79780..f8a2176 100644 --- a/v0/log.c +++ b/v0/log.c @@ -21,25 +21,31 @@ void log_error(const char* msg) { } void log_on_line(Location* loc, int to_column, const char* msg, ...) { + /* Declarations first for C89 */ char* line_prefix = NULL; char* formatted_msg = NULL; char* header = NULL; char* buffer = NULL; + va_list args; + int caret_len; + char* p; + int i1, i2; + size_t i3; + size_t total_size; line_prefix = format_string("%d| ", loc->line); if (!line_prefix) goto cleanup; - int caret_len = to_column - loc->column_start + 1; + caret_len = to_column - loc->column_start + 1; if (caret_len < 1) caret_len = 1; - // Format the message - va_list args; + /* Format the message */ va_start(args, msg); formatted_msg = format_string_va(msg, args); va_end(args); if (!formatted_msg) goto cleanup; - // Header logic + /* Header logic */ if (loc->filename && loc->filename[0] != '\0') { header = format_string("--- %s ---\n", loc->filename); } else { @@ -47,26 +53,26 @@ void log_on_line(Location* loc, int to_column, const char* msg, ...) { } if (!header) goto cleanup; - size_t total_size = strlen(header) + 20 + - strlen(line_prefix) + loc->line_text.length + 2 + // line| text\n - strlen(line_prefix) + loc->column_start - 1 + caret_len + 2 + // indent + ^^\n - strlen(line_prefix) + 3 + strlen(formatted_msg) + 2 + // indent + msg\n - 10; + total_size = strlen(header) + 20 + + strlen(line_prefix) + loc->line_text.length + 2 + /* line| text\n */ + strlen(line_prefix) + loc->column_start - 1 + caret_len + 2 + /* indent + ^^\n */ + strlen(line_prefix) + 3 + strlen(formatted_msg) + 2 + /* indent + msg\n */ + 10; buffer = (char*)malloc(total_size); if (!buffer) goto cleanup; - char* p = buffer; + p = buffer; p += sprintf(p, "%s", header); p += sprintf(p, "%s%.*s\n", line_prefix, (int)loc->line_text.length, loc->line_text.data); - // Caret line - for (int i = 0; i < (int)(strlen(line_prefix) + loc->column_start - 1); i++) *p++ = ' '; - for (int i = 0; i < caret_len; i++) *p++ = '^'; + /* Caret line */ + for (i1 = 0; i1 < (int)(strlen(line_prefix) + loc->column_start - 1); i1++) *p++ = ' '; + for (i2 = 0; i2 < caret_len; i2++) *p++ = '^'; *p++ = '\n'; - // Message line - for (size_t i = 0; i < strlen(line_prefix); i++) *p++ = ' '; + /* Message line */ + for (i3 = 0; i3 < strlen(line_prefix); i3++) *p++ = ' '; p += sprintf(p, "%s\n", formatted_msg); *p = '\0'; diff --git a/v0/main.c b/v0/main.c index 932a6c7..d161b83 100644 --- a/v0/main.c +++ b/v0/main.c @@ -1,5 +1,6 @@ #include int main(int argc, char** argv) { - puts("Hello, world"); -} \ No newline at end of file + puts("Hello, world"); + return 0; +} diff --git a/v0/parser.c b/v0/parser.c index fc55237..87c7a7d 100644 --- a/v0/parser.c +++ b/v0/parser.c @@ -5,26 +5,32 @@ #include Module* parser_parse(TokenStream* ts) { - Token t = tokenstream_next(ts); + /* Declarations first for C89 */ + Token t; + Module* module; + ImportDeclaration* new_imports; + int is_public; + + t = tokenstream_next(ts); if (t.token != TOKEN_MODULE) { log_on_line(&t.location, t.location.column_end, "expected 'module' keyword"); return NULL; } - t = tokenstream_next(ts); - if (t.token != TOKEN_IDENTIFIER) { - log_on_line(&t.location, t.location.column_end, "expected module name"); - return NULL; - } + t = tokenstream_next(ts); + if (t.token != TOKEN_IDENTIFIER) { + log_on_line(&t.location, t.location.column_end, "expected module name"); + return NULL; + } - Module* module = (Module*)malloc(sizeof(Module)); - if (module == NULL) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - module->name = NULL; - module->imports = NULL; - module->import_count = 0; + module = (Module*)malloc(sizeof(Module)); + if (module == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + module->name = NULL; + module->imports = NULL; + module->import_count = 0; module->name = (char*)malloc(t.text.length + 1); if (module->name == NULL) { @@ -51,19 +57,19 @@ Module* parser_parse(TokenStream* ts) { break; } - ImportDeclaration* new_imports = realloc(module->imports, (module->import_count + 1) * sizeof(ImportDeclaration)); - if (!new_imports) { - fprintf(stderr, "Out of memory\n"); - exit(1); - } - module->imports = new_imports; + new_imports = realloc(module->imports, (module->import_count + 1) * sizeof(ImportDeclaration)); + if (!new_imports) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } + module->imports = new_imports; t = tokenstream_next(ts); - bool is_public = false; - if (t.token == TOKEN_IDENTIFIER && strncmp(t.text.data, "public", t.text.length) == 0) { - is_public = true; - t = tokenstream_next(ts); - } + is_public = 0; + if (t.token == TOKEN_IDENTIFIER && strncmp(t.text.data, "public", t.text.length) == 0) { + is_public = 1; + t = tokenstream_next(ts); + } if (t.token != TOKEN_IDENTIFIER) { log_on_line(&t.location, t.location.column_end, "expected module name to import"); @@ -94,12 +100,14 @@ Module* parser_parse(TokenStream* ts) { void parser_free(Module* module) { if (module == NULL) return; - if (module->imports != NULL) { - for (size_t i = 0; i < module->import_count; i++) { - free(module->imports[i].module_name); - } - free(module->imports); - } + if (module->imports != NULL) { + /* C89: declare index variable before the loop */ + size_t i; + for (i = 0; i < module->import_count; i++) { + free(module->imports[i].module_name); + } + free(module->imports); + } free(module->name); free(module); } diff --git a/v0/test.c b/v0/test.c index c152e9d..ce48420 100644 --- a/v0/test.c +++ b/v0/test.c @@ -33,12 +33,16 @@ void assert_log(const char* expected, const char* msg) { } char* read_file_content(const char* filepath) { - FILE* f = fopen(filepath, "r"); + FILE* f; + long size; + char* content; + + f = fopen(filepath, "r"); if (!f) return NULL; fseek(f, 0, SEEK_END); - long size = ftell(f); + size = ftell(f); fseek(f, 0, SEEK_SET); - char* content = malloc(size + 1); + content = malloc(size + 1); if (!content) { fclose(f); return NULL; @@ -51,12 +55,13 @@ char* read_file_content(const char* filepath) { void assert_log_file(const char* msg) { char* filepath = format_string("v0/tests/%s.log", s_currentTestName); + const char* generate; + char* content; if (!filepath) { fail("out of memory"); return; } - - const char* generate = getenv("GENERATE_GOLDEN"); + generate = getenv("GENERATE_GOLDEN"); if (generate && strcmp(generate, "1") == 0) { FILE* f = fopen(filepath, "w"); if (!f) { @@ -68,7 +73,7 @@ void assert_log_file(const char* msg) { return; } - char* content = read_file_content(filepath); + content = read_file_content(filepath); if (!content) { fail("could not open golden file for reading"); free(filepath); @@ -105,7 +110,10 @@ void assert_false(bool condition, const char* msg) { } TokenStream* tokenstream_get_test(void) { - char* filepath = format_string("v0/tests/%s.c2", s_currentTestName); + char* filepath = NULL; + TokenStream* ts = NULL; + + filepath = format_string("v0/tests/%s.c2", s_currentTestName); if (!filepath) { fail("out of memory"); return NULL; @@ -118,7 +126,7 @@ TokenStream* tokenstream_get_test(void) { free(filepath); return NULL; } - TokenStream* ts = tokenstream_open(filepath, s_testSource); + ts = tokenstream_open(filepath, s_testSource); free(filepath); return ts; } @@ -139,7 +147,7 @@ static void log_append(const char* msg) { } } -static void log_clear() { +static void log_clear(void) { free(s_logOutput); s_logOutput = NULL; } @@ -180,16 +188,23 @@ static TestCase s_tests[] = { int main(int argc, char** argv) { + /* Declarations first for C89 */ + const char** failedTests; + int failedCount; + int i; + int j; + (void)argc; (void)argv; s_totalTests = sizeof(s_tests) / sizeof(s_tests[0]); s_greenTests = 0; - const char* failedTests[s_totalTests + 1]; - int failedCount = 0; + /* Allocate failed tests array dynamically to avoid VLAs */ + failedTests = (const char**)malloc((s_totalTests + 1) * sizeof(const char*)); + failedCount = 0; - for (int i = 0; i < s_totalTests; i++) { + for (i = 0; i < s_totalTests; i++) { s_currentTestName = s_tests[i].name; log_set_output(log_append); printf("%s...", s_tests[i].name); @@ -215,11 +230,12 @@ int main(int argc, char** argv) { if (failedCount > 0) { printf("\nFailed tests:\n"); - for (int i = 0; i < failedCount; i++) { - printf(" - %s\n", failedTests[i]); + for (j = 0; j < failedCount; j++) { + printf(" - %s\n", failedTests[j]); } } printf("\n%d/%d tests passed.\n", s_greenTests, s_totalTests); + free(failedTests); return failedCount > 0 ? 1 : 0; } diff --git a/v0/test.h b/v0/test.h index f46185a..ca74f1b 100644 --- a/v0/test.h +++ b/v0/test.h @@ -6,8 +6,6 @@ #include "token.h" -#include - typedef void (*Test)(void); /** @@ -56,6 +54,8 @@ void assert_int(int expected, int actual, const char* msg); /** * Asserts that a condition is true. */ +#include "bool.h" + void assert_true(bool condition, const char* msg); /** diff --git a/v0/test_log.c b/v0/test_log.c index 48ef572..58dac83 100644 --- a/v0/test_log.c +++ b/v0/test_log.c @@ -20,32 +20,32 @@ static void test_log_error(void) { assert_str("test error message", s_lastLoggedError, "expected 'test error message'"); - log_set_output(NULL); // Reset to default + log_set_output(NULL); free(s_lastLoggedError); s_lastLoggedError = NULL; } static void test_log_on_line(void) { - Location loc = { - .filename = "v0/tests/log_on_line.c2", - .line_text = { "int main() []", 13 }, - .line = 1, - .column_start = 12, - .column_end = 13 - }; + Location loc; + loc.filename = "v0/tests/log_on_line.c2"; + loc.line_text.data = "int main() []"; + loc.line_text.length = 13; + loc.line = 1; + loc.column_start = 12; + loc.column_end = 13; log_on_line(&loc, 13, "unexpected token"); assert_log_file("expected formatted error message"); } static void test_log_on_line_variadic(void) { - Location loc = { - .filename = "v0/tests/log_on_line_variadic.c2", - .line_text = { "int main() []", 13 }, - .line = 1, - .column_start = 12, - .column_end = 13 - }; + Location loc; + loc.filename = "v0/tests/log_on_line_variadic.c2"; + loc.line_text.data = "int main() []"; + loc.line_text.length = 13; + loc.line = 1; + loc.column_start = 12; + loc.column_end = 13; log_on_line(&loc, 13, "unexpected token '%c'", 'x'); assert_log_file("expected formatted error message with variadic args"); diff --git a/v0/test_token.c b/v0/test_token.c index 6790702..8b7ae5e 100644 --- a/v0/test_token.c +++ b/v0/test_token.c @@ -9,15 +9,19 @@ static void test_tokenstream_open_fail(void) { } static void test_tokenstream_simple_keyword(void) { - TokenStream* ts = tokenstream_open("test.c", "module"); + TokenStream* ts; + Token t; + Token eof; - Token t = tokenstream_next(ts); - if (t.token != TOKEN_MODULE) fail("expected TOKEN_MODULE"); + ts = tokenstream_open("test.c", "module"); - Token eof = tokenstream_next(ts); - if (eof.token != TOKEN_EOF) fail("expected EOF"); + t = tokenstream_next(ts); + if (t.token != TOKEN_MODULE) fail("expected TOKEN_MODULE"); - tokenstream_close(ts); + eof = tokenstream_next(ts); + if (eof.token != TOKEN_EOF) fail("expected EOF"); + + tokenstream_close(ts); } static void test_tokenstream_keywords_and_symbols(void) { @@ -93,29 +97,35 @@ static void test_tokenstream_unknown_token(void) { } static void test_tokenstream_info(void) { - TokenStream* ts = tokenstream_open("test.c", "module main;"); + TokenStream* ts; + Token t1; + Token t2; + char* buf1; + char* buf2; - Token t1 = tokenstream_next(ts); - if (t1.token != TOKEN_MODULE) fail("expected TOKEN_MODULE"); - - char* buf1 = malloc((size_t)t1.text.length + 1); + ts = tokenstream_open("test.c", "module main;"); + + t1 = tokenstream_next(ts); + if (t1.token != TOKEN_MODULE) fail("expected TOKEN_MODULE"); + + buf1 = malloc((size_t)t1.text.length + 1); if (!buf1) fail("out of memory"); memcpy(buf1, t1.text.data, t1.text.length); buf1[t1.text.length] = '\0'; assert_str("module", buf1, "info: expected 'module'"); - if (t1.location.line != 1) fail("expected line 1"); - if (t1.location.column_start != 1) fail("expected column 1"); + if (t1.location.line != 1) fail("expected line 1"); + if (t1.location.column_start != 1) fail("expected column 1"); - Token t2 = tokenstream_next(ts); - if (t2.token != TOKEN_IDENTIFIER) fail("expected TOKEN_IDENTIFIER"); - - char* buf2 = malloc((size_t)t2.text.length + 1); + t2 = tokenstream_next(ts); + if (t2.token != TOKEN_IDENTIFIER) fail("expected TOKEN_IDENTIFIER"); + + buf2 = malloc((size_t)t2.text.length + 1); if (!buf2) { free(buf1); fail("out of memory"); } memcpy(buf2, t2.text.data, t2.text.length); buf2[t2.text.length] = '\0'; assert_str("main", buf2, "info: expected 'main'"); - if (t2.location.line != 1) fail("expected line 1"); - if (t2.location.column_start != 8) fail("expected column 8"); + if (t2.location.line != 1) fail("expected line 1"); + if (t2.location.column_start != 8) fail("expected column 8"); free(buf1); free(buf2); diff --git a/v0/token.c b/v0/token.c index 69f706a..cc0307f 100644 --- a/v0/token.c +++ b/v0/token.c @@ -5,17 +5,17 @@ #include struct TokenStream { - char* filename; - const char* code; - size_t pos; - int line; - int column; - const char* line_start; + char* filename; + const char* code; + size_t pos; + int line; + int column; + const char* line_start; - // End of last non-EOF token - int last_line; - int last_column_end; - const char* last_line_start; + /* End of last non-EOF token */ + int last_line; + int last_column_end; + const char* last_line_start; }; /** @@ -38,8 +38,9 @@ static const KeywordMap keywords[] = { * Returns TOKEN_IDENTIFIER if not found. */ static TokenType lookup_keyword(const char* str, size_t length) { - int count = sizeof(keywords) / sizeof(keywords[0]); - for (int i = 0; i < count; i++) { + int count = sizeof(keywords) / sizeof(keywords[0]); + int i; + for (i = 0; i < count; i++) { if (strlen(keywords[i].keyword) == length && strncmp(keywords[i].keyword, str, length) == 0) { return keywords[i].token; @@ -117,27 +118,31 @@ static Token create_token(TokenStream* ts, TokenType type, const char* text, siz } TokenStream* tokenstream_open(const char* filename, const char* code) { - if (code == NULL) return NULL; + /* Declarations first for C89 */ + TokenStream* ts; + const char* name_src; - TokenStream* ts = (TokenStream*)malloc(sizeof(struct TokenStream)); - if (ts == NULL) { - return NULL; - } + if (code == NULL) return NULL; - const char* name_src = filename ? filename : "unknown"; + ts = (TokenStream*)malloc(sizeof(struct TokenStream)); + if (ts == NULL) { + return NULL; + } + + name_src = filename ? filename : "unknown"; ts->filename = malloc(strlen(name_src) + 1); if (ts->filename) { memcpy(ts->filename, name_src, strlen(name_src) + 1); } - ts->code = code; - ts->pos = 0; - ts->line = 1; - ts->column = 1; - ts->line_start = code; - ts->last_line = 1; - ts->last_column_end = 0; - ts->last_line_start = code; - return ts; + ts->code = code; + ts->pos = 0; + ts->line = 1; + ts->column = 1; + ts->line_start = code; + ts->last_line = 1; + ts->last_column_end = 0; + ts->last_line_start = code; + return ts; } void tokenstream_close(TokenStream* ts) { @@ -147,16 +152,22 @@ void tokenstream_close(TokenStream* ts) { } Token tokenstream_next(TokenStream* ts) { - if (ts == NULL) { - Token t = {0}; - t.token = TOKEN_EOF; - return t; - } + /* Declarations first for C89 */ + char c; + int start_line; + int start_column; + const char* line_start; + const char* start_text; + Token t; - char c; + if (ts == NULL) { + Token t = {0}; + t.token = TOKEN_EOF; + return t; + } - /* Skip whitespace and comments */ - while ((c = peek_char(ts)) != '\0') { + /* Skip whitespace and comments */ + while ((c = peek_char(ts)) != '\0') { if (isspace(c)) { read_char(ts); continue; @@ -194,12 +205,12 @@ Token tokenstream_next(TokenStream* ts) { return t; } - int start_line = ts->line; - int start_column = ts->column; - const char* line_start = ts->line_start; - const char* start_text = &ts->code[ts->pos]; + start_line = ts->line; + start_column = ts->column; + line_start = ts->line_start; + start_text = &ts->code[ts->pos]; - c = read_char(ts); + c = read_char(ts); /* Single-character tokens */ switch (c) { @@ -212,18 +223,22 @@ Token tokenstream_next(TokenStream* ts) { } /* Keywords and identifiers */ - if (is_identifier_start(c)) { - size_t length = 1; - while (is_identifier_part(peek_char(ts))) { - read_char(ts); - length++; - } - TokenType type = lookup_keyword(start_text, length); - return create_token(ts, type, start_text, length, start_line, start_column, line_start); - } + if (is_identifier_start(c)) { + /* Declarations first for C89 */ + size_t length; + TokenType type; + + length = 1; + while (is_identifier_part(peek_char(ts))) { + read_char(ts); + length++; + } + type = lookup_keyword(start_text, length); + return create_token(ts, type, start_text, length, start_line, start_column, line_start); + } /* Unknown character */ - Token t = create_token(ts, TOKEN_UNKNOWN, start_text, 1, start_line, start_column, line_start); + t = create_token(ts, TOKEN_UNKNOWN, start_text, 1, start_line, start_column, line_start); log_on_line(&t.location, t.location.column_end, "unexpected token '%c'", c); return t; } diff --git a/v0/token.h b/v0/token.h index 2614259..741c128 100644 --- a/v0/token.h +++ b/v0/token.h @@ -10,41 +10,41 @@ * A list of all possible tokens. */ typedef enum { - // Keywords - TOKEN_MODULE, + /* Keywords */ + TOKEN_MODULE, TOKEN_IMPORT, TOKEN_SEMICOLON, - // Symbols - TOKEN_PARENT_OPEN, + /* Symbols */ + TOKEN_PARENT_OPEN, TOKEN_PARENT_CLOSE, TOKEN_BRACKET_OPEN, TOKEN_BRACKET_CLOSE, TOKEN_COMMA, - // Primitives - TOKEN_VOID, + /* Primitives */ + TOKEN_VOID, - // Variable - TOKEN_IDENTIFIER, + /* Variable */ + TOKEN_IDENTIFIER, - // Others - TOKEN_EOF, - TOKEN_UNKNOWN, + /* Others */ + TOKEN_EOF, + TOKEN_UNKNOWN } TokenType; /** * Holds additional information about a token. */ typedef struct { - /// @brief The actual token. - TokenType token; + /* @brief The actual token. */ + TokenType token; - /// @brief The textual representation of a token. - String text; + /* @brief The textual representation of a token. */ + String text; - /// @brief The location of the token. - Location location; + /* @brief The location of the token. */ + Location location; } Token; typedef struct TokenStream TokenStream; @@ -71,4 +71,4 @@ void tokenstream_close(TokenStream* ts); */ Token tokenstream_next(TokenStream* ts); -#endif \ No newline at end of file +#endif diff --git a/v0/util.c b/v0/util.c index 0939a07..d1b28cb 100644 --- a/v0/util.c +++ b/v0/util.c @@ -2,26 +2,45 @@ #include #include #include +#include + +/* Portable va_copy fallback for pre-C99 or platforms without va_copy. */ +#ifndef va_copy +# if defined(__va_copy) +# define va_copy(dest, src) __va_copy(dest, src) +# else +# define va_copy(dest, src) ((dest) = (src)) +# endif +#endif char* format_string_va(const char* fmt, va_list args) { - if (!fmt) return NULL; + /* Declarations first to satisfy -std=c89 */ va_list args_copy; + int needed; + char* buf; + + if (!fmt) return NULL; + va_copy(args_copy, args); - int needed = vsnprintf(NULL, 0, fmt, args_copy); + needed = vsnprintf(NULL, 0, fmt, args_copy); va_end(args_copy); if (needed < 0) return NULL; - char* buf = (char*)malloc((size_t)needed + 1); + buf = (char*)malloc((size_t)needed + 1); if (!buf) return NULL; vsnprintf(buf, (size_t)needed + 1, fmt, args); return buf; } char* format_string(const char* fmt, ...) { - if (!fmt) return NULL; + /* Declarations first to satisfy -std=c89 */ va_list args; + char* s; + + if (!fmt) return NULL; + va_start(args, fmt); - char* s = format_string_va(fmt, args); + s = format_string_va(fmt, args); va_end(args); return s; }