Refactor parser to use Token in AST and update tests

This commit is contained in:
2026-04-29 10:35:12 +02:00
parent 146aa4d9d1
commit 15714393c3
3 changed files with 96 additions and 38 deletions
+57 -7
View File
@@ -5,29 +5,79 @@
#define AST_H #define AST_H
#include "bool.h" #include "bool.h"
#include "token.h"
#include <stddef.h> #include <stddef.h>
typedef struct { typedef struct {
/* @brief The name of the module being imported. */ /** @brief The name of the module being imported. */
char* module_name; Token module_name;
/* @brief Whether the import is public or not. */ /** @brief Whether the import is public or not. */
bool is_public; bool is_public;
} ImportDeclaration; } ImportDeclaration;
typedef enum {
TYPE_EXPRESSION_BUILTIN,
TYPE_EXPRESSION_ARRAY
} TypeExpressionTag;
/**
* An expression that evaluates to a type.
*/
typedef struct TypeExpression TypeExpression;
struct TypeExpression{
/** @brief defines which entry in the union is valid */
TypeExpressionTag tag;
union {
/** @brief Evaluates to an array of the given type. */
struct {
/** @brief A pointer to the type of the elements stored in the array. */
TypeExpression* array;
} array;
/** @brief Evaluates to a builtin integer type.*/
struct {
/**
* @brief The number of bits in the integer.
* Typical values are 8, 16, 32, and 64.
*/
int bitSize;
/** @brief `true` if the type is signed, `false` if it's unsigned. */
bool isSigned;
} builtin;
};
};
/**
* A declaration that aliases one type to another.
*/
typedef struct {
/** @brief The name of the alias. */
Token name;
/** @brief The value of the alias. */
TypeExpression value;
} AliasDeclaration;
/** /**
* The top-level model. * The top-level model.
* Every file matches an entire Module. * Every file matches an entire Module.
*/ */
typedef struct { typedef struct {
/* @brief The name of the module. */ /** @brief The name of the module. */
char* name; Token name;
/* @brief The list of imports in the module. */ /** @brief The list of imports in the module. */
ImportDeclaration* imports; ImportDeclaration* imports;
/* @brief The number of imports in the module. */ /** @brief The number of imports in the module. */
size_t import_count; size_t import_count;
/** @brief The list of aliases in the module. */
AliasDeclaration* aliases;
/** @brief The number of aliases in the module. */
size_t alias_count;
} Module; } Module;
#endif #endif
+8 -24
View File
@@ -28,18 +28,11 @@ Module* parser_parse(TokenStream* ts) {
fprintf(stderr, "Out of memory\n"); fprintf(stderr, "Out of memory\n");
exit(1); exit(1);
} }
module->name = NULL; module->name = t;
module->imports = NULL; module->imports = NULL;
module->import_count = 0; module->import_count = 0;
module->aliases = NULL;
module->name = (char*)malloc(t.text.length + 1); module->alias_count = 0;
if (module->name == NULL) {
fprintf(stderr, "Out of memory\n");
exit(1);
}
memcpy(module->name, t.text.data, t.text.length);
module->name[t.text.length] = '\0';
t = tokenstream_next(ts); t = tokenstream_next(ts);
if (t.token != TOKEN_SEMICOLON) { if (t.token != TOKEN_SEMICOLON) {
@@ -77,14 +70,8 @@ Module* parser_parse(TokenStream* ts) {
return NULL; return NULL;
} }
module->imports[module->import_count].module_name = (char*)malloc(t.text.length + 1); module->imports[module->import_count].module_name = t;
if (!module->imports[module->import_count].module_name) { module->imports[module->import_count].is_public = (bool)is_public;
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';
module->imports[module->import_count].is_public = is_public;
module->import_count++; module->import_count++;
t = tokenstream_next(ts); t = tokenstream_next(ts);
@@ -101,13 +88,10 @@ Module* parser_parse(TokenStream* ts) {
void parser_free(Module* module) { void parser_free(Module* module) {
if (module == NULL) return; if (module == NULL) return;
if (module->imports != NULL) { 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->imports);
} }
free(module->name); if (module->aliases != NULL) {
free(module->aliases);
}
free(module); free(module);
} }
+31 -7
View File
@@ -1,13 +1,19 @@
#include "test.h" #include "test.h"
#include "parser.h" #include "parser.h"
#include <string.h> #include <string.h>
#include <stdlib.h>
static void test_parser_module_name(void) { static void test_parser_module_name(void) {
TokenStream* ts = tokenstream_open("test.c", "module my_module;"); TokenStream* ts = tokenstream_open("test.c", "module my_module;");
Module* m = parser_parse(ts); Module* m = parser_parse(ts);
char* buf;
assert_not_null(m, "expected module to be parsed"); assert_not_null(m, "expected module to be parsed");
assert_str("my_module", m->name, "expected name 'my_module'"); buf = malloc(m->name.text.length + 1);
memcpy(buf, m->name.text.data, m->name.text.length);
buf[m->name.text.length] = '\0';
assert_str("my_module", buf, "expected name 'my_module'");
free(buf);
parser_free(m); parser_free(m);
tokenstream_close(ts); tokenstream_close(ts);
@@ -56,13 +62,22 @@ static void test_parser_bad_import_name(void) {
static void test_parser_imports(void) { static void test_parser_imports(void) {
TokenStream* ts = tokenstream_open("test.c", "module my_module; import other_module;"); TokenStream* ts = tokenstream_open("test.c", "module my_module; import other_module;");
Module* m = parser_parse(ts); Module* m = parser_parse(ts);
char* buf;
assert_not_null(m, "expected module to be parsed"); assert_not_null(m, "expected module to be parsed");
assert_str("my_module", m->name, "expected name 'my_module'"); buf = malloc(m->name.text.length + 1);
memcpy(buf, m->name.text.data, m->name.text.length);
buf[m->name.text.length] = '\0';
assert_str("my_module", buf, "expected name 'my_module'");
free(buf);
assert_not_null(m->imports, "expected imports to be parsed"); assert_not_null(m->imports, "expected imports to be parsed");
assert_int(1, m->import_count, "expected one import"); assert_int(1, (int)m->import_count, "expected one import");
assert_str("other_module", m->imports[0].module_name, "expected import name 'other_module'"); buf = malloc(m->imports[0].module_name.text.length + 1);
memcpy(buf, m->imports[0].module_name.text.data, m->imports[0].module_name.text.length);
buf[m->imports[0].module_name.text.length] = '\0';
assert_str("other_module", buf, "expected import name 'other_module'");
free(buf);
assert_false(m->imports[0].is_public, "expected import to not be public"); assert_false(m->imports[0].is_public, "expected import to not be public");
parser_free(m); parser_free(m);
@@ -72,13 +87,22 @@ static void test_parser_imports(void) {
static void test_parser_public_imports(void) { static void test_parser_public_imports(void) {
TokenStream* ts = tokenstream_open("test.c", "module my_module; import public other_module;"); TokenStream* ts = tokenstream_open("test.c", "module my_module; import public other_module;");
Module* m = parser_parse(ts); Module* m = parser_parse(ts);
char* buf;
assert_not_null(m, "expected module to be parsed"); assert_not_null(m, "expected module to be parsed");
assert_str("my_module", m->name, "expected name 'my_module'"); buf = malloc(m->name.text.length + 1);
memcpy(buf, m->name.text.data, m->name.text.length);
buf[m->name.text.length] = '\0';
assert_str("my_module", buf, "expected name 'my_module'");
free(buf);
assert_not_null(m->imports, "expected imports to be parsed"); assert_not_null(m->imports, "expected imports to be parsed");
assert_int(1, m->import_count, "expected one import"); assert_int(1, (int)m->import_count, "expected one import");
assert_str("other_module", m->imports[0].module_name, "expected import name 'other_module'"); buf = malloc(m->imports[0].module_name.text.length + 1);
memcpy(buf, m->imports[0].module_name.text.data, m->imports[0].module_name.text.length);
buf[m->imports[0].module_name.text.length] = '\0';
assert_str("other_module", buf, "expected import name 'other_module'");
free(buf);
assert_true(m->imports[0].is_public, "expected import to be public"); assert_true(m->imports[0].is_public, "expected import to be public");
parser_free(m); parser_free(m);