More variable stuff

This commit is contained in:
2026-04-30 20:25:53 +02:00
parent 0704284726
commit 4bd66ea216
10 changed files with 144 additions and 14 deletions
+20 -5
View File
@@ -17,6 +17,21 @@ typedef struct {
bool is_public; bool is_public;
} ImportDeclaration; } ImportDeclaration;
typedef enum {
EXPRESSION_INTEGER,
EXPRESSION_STRING,
EXPRESSION_BOOLEAN
} ExpressionTag;
typedef struct {
ExpressionTag tag;
union {
int integer;
const char* string;
bool boolean;
};
} Expression;
typedef enum { typedef enum {
TYPE_EXPRESSION_BUILTIN, TYPE_EXPRESSION_BUILTIN,
TYPE_EXPRESSION_ARRAY TYPE_EXPRESSION_ARRAY
@@ -26,7 +41,7 @@ typedef enum {
* An expression that evaluates to a type. * An expression that evaluates to a type.
*/ */
typedef struct TypeExpression TypeExpression; typedef struct TypeExpression TypeExpression;
struct TypeExpression{ struct TypeExpression {
/** @brief defines which entry in the union is valid */ /** @brief defines which entry in the union is valid */
TypeExpressionTag tag; TypeExpressionTag tag;
union { union {
@@ -53,13 +68,10 @@ struct TypeExpression{
*/ */
typedef struct { typedef struct {
/** @brief The name of the alias. */ /** @brief The name of the alias. */
char* name; const char* name;
/** @brief The value of the alias. */ /** @brief The value of the alias. */
TypeExpression value; TypeExpression value;
/** @brief Whether the import is public or not. */
bool is_public;
} AliasDeclaration; } AliasDeclaration;
/** /**
@@ -72,6 +84,9 @@ typedef struct {
/** @brief The type of the variable. */ /** @brief The type of the variable. */
TypeExpression type; TypeExpression type;
/** @brief The optional initializer expression. */
Expression* initializer;
/** @brief Whether the variable is public or not. */ /** @brief Whether the variable is public or not. */
bool is_public; bool is_public;
+39 -9
View File
@@ -242,7 +242,6 @@ static bool parse_alias_declaration(Parser* p, Module* module, bool is_public) {
AliasDeclaration* alias = &module->aliases[module->alias_count - 1]; AliasDeclaration* alias = &module->aliases[module->alias_count - 1];
memset(alias, 0, sizeof(AliasDeclaration)); memset(alias, 0, sizeof(AliasDeclaration));
alias->is_public = is_public;
if (!parser_require(p, TOKEN_IDENTIFIER, "expected alias identifier")) { if (!parser_require(p, TOKEN_IDENTIFIER, "expected alias identifier")) {
return false; return false;
@@ -264,15 +263,33 @@ static bool parse_alias_declaration(Parser* p, Module* module, bool is_public) {
return true; return true;
} }
/**
* Parses an expression.
*/
static bool parse_expression(Parser* p, Expression* expr) {
if (parser_accept(p, TOKEN_INTEGER)) {
expr->tag = EXPRESSION_INTEGER;
expr->integer = atoi(p->token.text.data);
return true;
} else if (parser_accept(p, TOKEN_STRING)) {
expr->tag = EXPRESSION_STRING;
expr->string = parser_to_text(p);
return true;
} else if (parser_accept(p, TOKEN_TRUE)) {
expr->tag = EXPRESSION_BOOLEAN;
expr->boolean = true;
return true;
} else if (parser_accept(p, TOKEN_FALSE)) {
expr->tag = EXPRESSION_BOOLEAN;
expr->boolean = false;
return true;
}
log_on_line(&p->token.location, "expected expression");
return false;
}
/** /**
* Parses a variable declaration. * Parses a variable declaration.
*
* @param p The parser state.
* @param module The module to add the variable to.
* @param is_public Whether the variable is public.
* @param is_static Whether the variable is static.
* @param is_const Whether the variable is constant.
* @return true if successful, false otherwise.
*/ */
static bool parse_variable_declaration(Parser* p, Module* module, bool is_public, bool is_static, bool is_const) { static bool parse_variable_declaration(Parser* p, Module* module, bool is_public, bool is_static, bool is_const) {
module->variable_count++; module->variable_count++;
@@ -293,6 +310,13 @@ static bool parse_variable_declaration(Parser* p, Module* module, bool is_public
} }
var->name = parser_to_text(p); var->name = parser_to_text(p);
if (parser_accept(p, TOKEN_ASSIGN)) {
var->initializer = malloc(sizeof(Expression));
if (!parse_expression(p, var->initializer)) {
return false;
}
}
if (!parser_expect(p, TOKEN_SEMICOLON, "expected ';' after variable declaration")) { if (!parser_expect(p, TOKEN_SEMICOLON, "expected ';' after variable declaration")) {
return false; return false;
} }
@@ -406,7 +430,7 @@ void parser_free(Module* module) {
if (module->aliases != NULL) { if (module->aliases != NULL) {
for(size_t i = 0; i < module->alias_count; i++) { for(size_t i = 0; i < module->alias_count; i++) {
free(module->aliases[i].name); free((void*)module->aliases[i].name);
free_type_expression(&module->aliases[i].value); free_type_expression(&module->aliases[i].value);
} }
free(module->aliases); free(module->aliases);
@@ -416,6 +440,12 @@ void parser_free(Module* module) {
for(size_t i = 0; i < module->variable_count; i++) { for(size_t i = 0; i < module->variable_count; i++) {
free(module->variables[i].name); free(module->variables[i].name);
free_type_expression(&module->variables[i].type); free_type_expression(&module->variables[i].type);
if (module->variables[i].initializer) {
if (module->variables[i].initializer->tag == EXPRESSION_STRING) {
free((void*)module->variables[i].initializer->string);
}
free(module->variables[i].initializer);
}
} }
free(module->variables); free(module->variables);
} }
+4
View File
@@ -211,6 +211,10 @@ static TestCase s_tests[] = {
TEST(test_parser_module_name) TEST(test_parser_module_name)
TEST(test_parser_public_imports) TEST(test_parser_public_imports)
TEST(test_parser_variable_simple) TEST(test_parser_variable_simple)
TEST(test_parser_variable_const)
TEST(test_parser_variable_init)
TEST(test_parser_variable_static)
TEST(test_parser_multiple_vars)
TEST(test_tokenstream_comma) TEST(test_tokenstream_comma)
TEST(test_tokenstream_info) TEST(test_tokenstream_info)
TEST(test_tokenstream_keywords_and_symbols) TEST(test_tokenstream_keywords_and_symbols)
+44
View File
@@ -95,3 +95,47 @@ static void test_parser_variable_simple(void) {
assert_false(var.is_static, "expected not static"); assert_false(var.is_static, "expected not static");
} }
static void test_parser_variable_const(void) {
Module* m = test_get_ast();
assert_not_null(m, "expected module to be parsed");
assert_int(1, (int)m->variable_count, "expected correct number of variables");
VariableDeclaration var = m->variables[0];
assert_str("my_const", var.name, "expected correct variable name");
assert_true(var.is_const, "expected const");
assert_false(var.is_static, "expected not static");
}
static void test_parser_variable_static(void) {
Module* m = test_get_ast();
assert_not_null(m, "expected module to be parsed");
assert_int(1, (int)m->variable_count, "expected correct number of variables");
VariableDeclaration var = m->variables[0];
assert_str("my_static", var.name, "expected correct variable name");
assert_false(var.is_const, "expected not const");
assert_true(var.is_static, "expected static");
}
static void test_parser_multiple_vars(void) {
Module* m = test_get_ast();
assert_not_null(m, "expected module to be parsed");
assert_int(2, (int)m->variable_count, "expected correct number of variables");
assert_str("var1", m->variables[0].name, "expected first variable name 'var1'");
assert_str("var2", m->variables[1].name, "expected second variable name 'var2'");
}
static void test_parser_variable_init(void) {
Module* m = test_get_ast();
assert_not_null(m, "expected module to be parsed");
assert_int(1, (int)m->variable_count, "expected 1 variable");
VariableDeclaration* var = &m->variables[0];
assert_str("x", var->name, "expected variable name 'x'");
assert_not_null(var->initializer, "expected variable to have an initializer");
assert_int(EXPRESSION_INTEGER, var->initializer->tag, "expected integer initializer");
assert_int(123, var->initializer->integer, "expected value 123");
parser_free(m);
}
+4
View File
@@ -0,0 +1,4 @@
module test_multiple_vars;
i32 var1;
i32 var2;
+3
View File
@@ -0,0 +1,3 @@
module test_const_var;
const i32 my_const;
+2
View File
@@ -0,0 +1,2 @@
module mymodule;
var x = 123;
+3
View File
@@ -0,0 +1,3 @@
module test_static_var;
static i32 my_static;
+21
View File
@@ -44,6 +44,8 @@ static const KeywordMap keywords[] = {
{"u16", TOKEN_U16}, {"u16", TOKEN_U16},
{"u32", TOKEN_U32}, {"u32", TOKEN_U32},
{"u64", TOKEN_U64}, {"u64", TOKEN_U64},
{"true", TOKEN_TRUE},
{"false", TOKEN_FALSE},
}; };
/** /**
@@ -234,8 +236,27 @@ Token tokenstream_next(TokenStream* ts) {
case ',': return create_token(ts, TOKEN_COMMA, start_text, 1, start_line, start_column, line_start); case ',': return create_token(ts, TOKEN_COMMA, start_text, 1, start_line, start_column, line_start);
case ';': return create_token(ts, TOKEN_SEMICOLON, start_text, 1, start_line, start_column, line_start); case ';': return create_token(ts, TOKEN_SEMICOLON, start_text, 1, start_line, start_column, line_start);
case '=': return create_token(ts, TOKEN_ASSIGN, start_text, 1, start_line, start_column, line_start); case '=': return create_token(ts, TOKEN_ASSIGN, start_text, 1, start_line, start_column, line_start);
case '"': {
size_t len = 0;
const char* start = &ts->code[ts->pos];
while (peek_char(ts) != '"' && peek_char(ts) != '\0') {
read_char(ts);
len++;
}
if (peek_char(ts) == '"') read_char(ts);
return create_token(ts, TOKEN_STRING, start, len, start_line, start_column + 1, line_start);
}
} }
if (isdigit(c)) {
size_t len = 1;
while (isdigit(peek_char(ts))) {
read_char(ts);
len++;
}
return create_token(ts, TOKEN_INTEGER, start_text, len, start_line, start_column, line_start);
}
/* Keywords and identifiers */ /* Keywords and identifiers */
if (is_identifier_start(c)) { if (is_identifier_start(c)) {
/* Declarations first for C89 */ /* Declarations first for C89 */
+4
View File
@@ -38,6 +38,10 @@ typedef enum {
TOKEN_U16, TOKEN_U16,
TOKEN_U32, TOKEN_U32,
TOKEN_U64, TOKEN_U64,
TOKEN_STRING,
TOKEN_INTEGER,
TOKEN_TRUE,
TOKEN_FALSE,
/* Variable */ /* Variable */
TOKEN_IDENTIFIER, TOKEN_IDENTIFIER,