From 91593e12b7c0c3cff0253948f7912c72468dfb27 Mon Sep 17 00:00:00 2001 From: Sebastiaan de Schaetzen Date: Sat, 25 Apr 2026 14:37:08 +0200 Subject: [PATCH] Add error logging and corresponding tests for parser syntax errors --- v0/parser.c | 27 ++++++++++++++------- v0/test.c | 4 ++++ v0/test_parser.c | 34 +++++++++++++++++++++++++-- v0/tests/bad_import_name.txt | 4 ++++ v0/tests/bad_module_log.txt | 4 ---- v0/tests/bad_module_name.txt | 4 ++++ v0/tests/missing_semicolon_import.txt | 4 ++++ v0/tests/missing_semicolon_module.txt | 4 ++++ 8 files changed, 70 insertions(+), 15 deletions(-) create mode 100644 v0/tests/bad_import_name.txt delete mode 100644 v0/tests/bad_module_log.txt create mode 100644 v0/tests/bad_module_name.txt create mode 100644 v0/tests/missing_semicolon_import.txt create mode 100644 v0/tests/missing_semicolon_module.txt diff --git a/v0/parser.c b/v0/parser.c index 3887d20..961e61c 100644 --- a/v0/parser.c +++ b/v0/parser.c @@ -1,25 +1,32 @@ #include "parser.h" +#include "log.h" #include #include +#include Module* parser_parse(TokenStream* ts) { Token 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; } Module* module = (Module*)malloc(sizeof(Module)); - if (module == NULL) return NULL; + if (module == NULL) { + fprintf(stderr, "Out of memory\n"); + exit(1); + } module->name = (char*)malloc(t.text.length + 1); if (module->name == NULL) { - free(module); - return NULL; + fprintf(stderr, "Out of memory\n"); + exit(1); } memcpy(module->name, t.text.data, t.text.length); @@ -27,8 +34,8 @@ Module* parser_parse(TokenStream* ts) { t = tokenstream_next(ts); if (t.token != TOKEN_SEMICOLON) { - free(module->name); - free(module); + log_on_line(&t.location, t.location.column_end, "expected ';' after module name"); + parser_free(module); return NULL; } @@ -43,21 +50,22 @@ Module* parser_parse(TokenStream* ts) { ImportDeclaration* new_imports = realloc(module->imports, (module->import_count + 1) * sizeof(ImportDeclaration)); if (!new_imports) { - parser_free(module); - return NULL; + fprintf(stderr, "Out of memory\n"); + exit(1); } module->imports = new_imports; t = tokenstream_next(ts); if (t.token != TOKEN_IDENTIFIER) { + log_on_line(&t.location, t.location.column_end, "expected module name to import"); parser_free(module); return NULL; } module->imports[module->import_count].module_name = (char*)malloc(t.text.length + 1); if (!module->imports[module->import_count].module_name) { - parser_free(module); - return NULL; + fprintf(stderr, "Out of memory\n"); + exit(1); } memcpy(module->imports[module->import_count].module_name, t.text.data, t.text.length); module->imports[module->import_count].module_name[t.text.length] = '\0'; @@ -65,6 +73,7 @@ Module* parser_parse(TokenStream* ts) { t = tokenstream_next(ts); if (t.token != TOKEN_SEMICOLON) { + log_on_line(&t.location, t.location.column_end, "expected ';' after import"); parser_free(module); return NULL; } diff --git a/v0/test.c b/v0/test.c index f90f33e..429a81c 100644 --- a/v0/test.c +++ b/v0/test.c @@ -119,6 +119,10 @@ static TestCase s_tests[] = { {"tokenstream_unknown_token", test_tokenstream_unknown_token}, {"tokenstream_info", test_tokenstream_info}, {"parser_module_name", test_parser_module_name}, + {"parser_bad_module_name", test_parser_bad_module_name}, + {"parser_missing_semicolon_module", test_parser_missing_semicolon_module}, + {"parser_missing_semicolon_import", test_parser_missing_semicolon_import}, + {"parser_bad_import_name", test_parser_bad_import_name}, {"parser_imports", test_parser_imports}, {"log_error", test_log_error}, {"log_on_line", test_log_on_line}, diff --git a/v0/test_parser.c b/v0/test_parser.c index 2d8d428..704696d 100644 --- a/v0/test_parser.c +++ b/v0/test_parser.c @@ -17,7 +17,37 @@ static void test_parser_bad_module_name(void) { TokenStream* ts = tokenstream_open("test.c", "import other_module;"); Module* m = parser_parse(ts); - assert_log_file("bad_module_name.log", "expected error to be logged for bad module name"); + assert_log_file("v0/tests/bad_module_name.txt", "expected error to be logged for bad module name"); + + parser_free(m); + tokenstream_close(ts); +} + +static void test_parser_missing_semicolon_module(void) { + TokenStream* ts = tokenstream_open("test.c", "module my_module"); + Module* m = parser_parse(ts); + + assert_log_file("v0/tests/missing_semicolon_module.txt", "expected error for missing semicolon"); + + parser_free(m); + tokenstream_close(ts); +} + +static void test_parser_missing_semicolon_import(void) { + TokenStream* ts = tokenstream_open("test.c", "module my_module; import other_module"); + Module* m = parser_parse(ts); + + assert_log_file("v0/tests/missing_semicolon_import.txt", "expected error for missing semicolon"); + + parser_free(m); + tokenstream_close(ts); +} + +static void test_parser_bad_import_name(void) { + TokenStream* ts = tokenstream_open("test.c", "module my_module; import ;"); + Module* m = parser_parse(ts); + + assert_log_file("v0/tests/bad_import_name.txt", "expected error for bad import name"); parser_free(m); tokenstream_close(ts); @@ -36,4 +66,4 @@ static void test_parser_imports(void) { parser_free(m); tokenstream_close(ts); -} \ No newline at end of file +} diff --git a/v0/tests/bad_import_name.txt b/v0/tests/bad_import_name.txt new file mode 100644 index 0000000..dff0f45 --- /dev/null +++ b/v0/tests/bad_import_name.txt @@ -0,0 +1,4 @@ +--- test.c --- +1| module my_module; import ; + ^ + expected module name to import diff --git a/v0/tests/bad_module_log.txt b/v0/tests/bad_module_log.txt deleted file mode 100644 index afced76..0000000 --- a/v0/tests/bad_module_log.txt +++ /dev/null @@ -1,4 +0,0 @@ ---- test.c --- -1| import other_module.c - ^^^^^^ - expected `module` diff --git a/v0/tests/bad_module_name.txt b/v0/tests/bad_module_name.txt new file mode 100644 index 0000000..26506fb --- /dev/null +++ b/v0/tests/bad_module_name.txt @@ -0,0 +1,4 @@ +--- test.c --- +1| import other_module; + ^^^^^^ + expected 'module' keyword diff --git a/v0/tests/missing_semicolon_import.txt b/v0/tests/missing_semicolon_import.txt new file mode 100644 index 0000000..d6d2947 --- /dev/null +++ b/v0/tests/missing_semicolon_import.txt @@ -0,0 +1,4 @@ +--- test.c --- +1| module my_module; import other_module + ^ + expected ';' after import diff --git a/v0/tests/missing_semicolon_module.txt b/v0/tests/missing_semicolon_module.txt new file mode 100644 index 0000000..585f78a --- /dev/null +++ b/v0/tests/missing_semicolon_module.txt @@ -0,0 +1,4 @@ +--- test.c --- +1| module my_module + ^ + expected ';' after module name