Add import parsing

Co-authored-by: Copilot <copilot@github.com>
This commit is contained in:
2026-04-25 14:30:11 +02:00
parent 116bdecafe
commit 7c7e0c3272
6 changed files with 100 additions and 0 deletions
+13
View File
@@ -4,6 +4,13 @@
#ifndef AST_H #ifndef AST_H
#define AST_H #define AST_H
#include <stddef.h>
typedef struct {
/// @brief The name of the module being imported.
char* module_name;
} ImportDeclaration;
/** /**
* The top-level model. * The top-level model.
* Every file matches an entire Module. * Every file matches an entire Module.
@@ -11,6 +18,12 @@
typedef struct { typedef struct {
/// @brief The name of the module. /// @brief The name of the module.
char* name; char* name;
/// @brief The list of imports in the module.
ImportDeclaration* imports;
/// @brief The number of imports in the module.
size_t import_count;
} Module; } Module;
#endif #endif
+44
View File
@@ -32,11 +32,55 @@ Module* parser_parse(TokenStream* ts) {
return NULL; return NULL;
} }
module->imports = NULL;
module->import_count = 0;
while (1) {
t = tokenstream_next(ts);
if (t.token != TOKEN_IMPORT) {
break;
}
ImportDeclaration* new_imports = realloc(module->imports, (module->import_count + 1) * sizeof(ImportDeclaration));
if (!new_imports) {
parser_free(module);
return NULL;
}
module->imports = new_imports;
t = tokenstream_next(ts);
if (t.token != TOKEN_IDENTIFIER) {
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;
}
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';
module->import_count++;
t = tokenstream_next(ts);
if (t.token != TOKEN_SEMICOLON) {
parser_free(module);
return NULL;
}
}
return module; return module;
} }
void parser_free(Module* module) { void parser_free(Module* module) {
if (module == NULL) return; 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);
}
free(module->name); free(module->name);
free(module); free(module);
} }
+9
View File
@@ -67,6 +67,14 @@ void assert_log_file(const char* filepath, const char* msg) {
free(content); free(content);
} }
void assert_int(int expected, int actual, const char* msg) {
if (expected != actual) {
char buf[64];
snprintf(buf, sizeof(buf), "%s (expected %d, got %d)", msg, expected, actual);
fail(buf);
}
}
static void log_append(const char* msg) { static void log_append(const char* msg) {
size_t oldLen = s_logOutput ? strlen(s_logOutput) : 0; size_t oldLen = s_logOutput ? strlen(s_logOutput) : 0;
size_t newLen = oldLen + strlen(msg) + 1; size_t newLen = oldLen + strlen(msg) + 1;
@@ -111,6 +119,7 @@ static TestCase s_tests[] = {
{"tokenstream_unknown_token", test_tokenstream_unknown_token}, {"tokenstream_unknown_token", test_tokenstream_unknown_token},
{"tokenstream_info", test_tokenstream_info}, {"tokenstream_info", test_tokenstream_info},
{"parser_module_name", test_parser_module_name}, {"parser_module_name", test_parser_module_name},
{"parser_imports", test_parser_imports},
{"log_error", test_log_error}, {"log_error", test_log_error},
{"log_on_line", test_log_on_line}, {"log_on_line", test_log_on_line},
}; };
+5
View File
@@ -44,4 +44,9 @@ void assert_log(const char* expected, const char* msg);
*/ */
void assert_log_file(const char* filepath, const char* msg); void assert_log_file(const char* filepath, const char* msg);
/**
* Asserts that two integers are equal.
*/
void assert_int(int expected, int actual, const char* msg);
#endif #endif
+25
View File
@@ -12,3 +12,28 @@ static void test_parser_module_name(void) {
parser_free(m); parser_free(m);
tokenstream_close(ts); tokenstream_close(ts);
} }
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");
parser_free(m);
tokenstream_close(ts);
}
static void test_parser_imports(void) {
TokenStream* ts = tokenstream_open("test.c", "module my_module; import other_module;");
Module* m = parser_parse(ts);
assert_not_null(m, "expected module to be parsed");
assert_str("my_module", m->name, "expected name 'my_module'");
assert_not_null(m->imports, "expected imports to be parsed");
assert_int(1, m->import_count, "expected one import");
assert_str("other_module", m->imports[0].module_name, "expected import name 'other_module'");
parser_free(m);
tokenstream_close(ts);
}
+4
View File
@@ -0,0 +1,4 @@
--- test.c ---
1| import other_module.c
^^^^^^
expected `module`