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;
} 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 {
TYPE_EXPRESSION_BUILTIN,
TYPE_EXPRESSION_ARRAY
@@ -26,7 +41,7 @@ typedef enum {
* An expression that evaluates to a type.
*/
typedef struct TypeExpression TypeExpression;
struct TypeExpression{
struct TypeExpression {
/** @brief defines which entry in the union is valid */
TypeExpressionTag tag;
union {
@@ -53,13 +68,10 @@ struct TypeExpression{
*/
typedef struct {
/** @brief The name of the alias. */
char* name;
const char* name;
/** @brief The value of the alias. */
TypeExpression value;
/** @brief Whether the import is public or not. */
bool is_public;
} AliasDeclaration;
/**
@@ -72,6 +84,9 @@ typedef struct {
/** @brief The type of the variable. */
TypeExpression type;
/** @brief The optional initializer expression. */
Expression* initializer;
/** @brief Whether the variable is public or not. */
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];
memset(alias, 0, sizeof(AliasDeclaration));
alias->is_public = is_public;
if (!parser_require(p, TOKEN_IDENTIFIER, "expected alias identifier")) {
return false;
@@ -264,15 +263,33 @@ static bool parse_alias_declaration(Parser* p, Module* module, bool is_public) {
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.
*
* @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) {
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);
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")) {
return false;
}
@@ -406,7 +430,7 @@ void parser_free(Module* module) {
if (module->aliases != NULL) {
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(module->aliases);
@@ -416,6 +440,12 @@ void parser_free(Module* module) {
for(size_t i = 0; i < module->variable_count; i++) {
free(module->variables[i].name);
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);
}
+4
View File
@@ -211,6 +211,10 @@ static TestCase s_tests[] = {
TEST(test_parser_module_name)
TEST(test_parser_public_imports)
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_info)
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");
}
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},
{"u32", TOKEN_U32},
{"u64", TOKEN_U64},
{"true", TOKEN_TRUE},
{"false", TOKEN_FALSE},
};
/**
@@ -234,6 +236,25 @@ 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_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 '"': {
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 */
+4
View File
@@ -38,6 +38,10 @@ typedef enum {
TOKEN_U16,
TOKEN_U32,
TOKEN_U64,
TOKEN_STRING,
TOKEN_INTEGER,
TOKEN_TRUE,
TOKEN_FALSE,
/* Variable */
TOKEN_IDENTIFIER,