Can parse variables

This commit is contained in:
2026-04-29 21:39:48 +02:00
parent 94ae665a0a
commit 0704284726
5 changed files with 108 additions and 15 deletions
+2 -2
View File
@@ -6,10 +6,10 @@ Global variables can be defined as such:
```c2 ```c2
// Defines a global variable called my_var. // Defines a global variable called my_var.
int32 my_var; i32 my_var;
// Defines a const variable. // Defines a const variable.
const int32 my_var; const i32 my_var;
// Defines a global variable whose type is determined automatically. // Defines a global variable whose type is determined automatically.
// The value will be determined at runtime. // The value will be determined at runtime.
+89 -2
View File
@@ -264,6 +264,57 @@ static bool parse_alias_declaration(Parser* p, Module* module, bool is_public) {
return true; return true;
} }
/**
* 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++;
module->variables = realloc(module->variables, sizeof(VariableDeclaration) * module->variable_count);
VariableDeclaration* var = &module->variables[module->variable_count - 1];
memset(var, 0, sizeof(VariableDeclaration));
var->is_public = is_public;
var->is_static = is_static;
var->is_const = is_const;
if (!parse_type_expression(p, &var->type)) {
return false;
}
if (!parser_require(p, TOKEN_IDENTIFIER, "expected variable identifier")) {
return false;
}
var->name = parser_to_text(p);
if (!parser_expect(p, TOKEN_SEMICOLON, "expected ';' after variable declaration")) {
return false;
}
return true;
}
/**
* Checks if the current token is a primitive type.
*
* The token will not be consumed by this function, so the caller can decide how to handle it if it is a primitive type.
*
* @param p The parser state.
* @return true if the current token is a primitive type, false otherwise.
*/
static bool parser_accept_primitive(Parser* p) {
return parser_peek(p, TOKEN_I8) || parser_peek(p, TOKEN_I16) ||
parser_peek(p, TOKEN_I32) || parser_peek(p, TOKEN_I64) ||
parser_peek(p, TOKEN_U8) || parser_peek(p, TOKEN_U16) ||
parser_peek(p, TOKEN_U32) || parser_peek(p, TOKEN_U64);
}
Module* parser_parse(TokenStream* ts) { Module* parser_parse(TokenStream* ts) {
Parser* p = malloc(sizeof(Parser)); Parser* p = malloc(sizeof(Parser));
p->ts = ts; p->ts = ts;
@@ -277,25 +328,53 @@ Module* parser_parse(TokenStream* ts) {
while (!parser_peek(p, TOKEN_EOF)) { while (!parser_peek(p, TOKEN_EOF)) {
bool is_public = false; bool is_public = false;
bool is_static = false;
bool is_const = false;
bool terminal = false; bool terminal = false;
do {
while (!terminal) {
if (parser_accept(p, TOKEN_IMPORT)) { if (parser_accept(p, TOKEN_IMPORT)) {
if (is_static) {
log_on_line(&p->token.location, "import declarations cannot be static or const");
goto fail;
}
if (is_const) {
log_on_line(&p->token.location, "import declarations cannot be static or const");
goto fail;
}
if (!parse_import_declaration(p, module, is_public)) { if (!parse_import_declaration(p, module, is_public)) {
goto fail; goto fail;
} }
terminal = true; terminal = true;
} else if (parser_accept(p, TOKEN_ALIAS)) { } else if (parser_accept(p, TOKEN_ALIAS)) {
if (is_static) {
log_on_line(&p->token.location, "alias declarations cannot be static or const");
goto fail;
}
if (is_const) {
log_on_line(&p->token.location, "alias declarations cannot be static or const");
goto fail;
}
if (!parse_alias_declaration(p, module, is_public)) { if (!parse_alias_declaration(p, module, is_public)) {
goto fail; goto fail;
} }
terminal = true; terminal = true;
} else if (parser_accept(p, TOKEN_PUBLIC)) { } else if (parser_accept(p, TOKEN_PUBLIC)) {
is_public = true; is_public = true;
} else if (parser_accept(p, TOKEN_STATIC)) {
is_static = true;
} else if (parser_accept(p, TOKEN_CONST)) {
is_const = true;
} else if (parser_accept_primitive(p)) {
if (!parse_variable_declaration(p, module, is_public, is_static, is_const)) {
goto fail;
}
terminal = true;
} else { } else {
log_on_line(&p->token.location, "unexpected token"); log_on_line(&p->token.location, "unexpected token");
goto fail; goto fail;
} }
} while (!terminal); }
} }
free(p); free(p);
@@ -333,6 +412,14 @@ void parser_free(Module* module) {
free(module->aliases); free(module->aliases);
} }
if (module->variables != NULL) {
for(size_t i = 0; i < module->variable_count; i++) {
free(module->variables[i].name);
free_type_expression(&module->variables[i].type);
}
free(module->variables);
}
free(module->name); free(module->name);
free(module); free(module);
} }
+1 -1
View File
@@ -201,7 +201,6 @@ static TestCase s_tests[] = {
TEST(test_log_error) TEST(test_log_error)
TEST(test_log_on_line_variadic) TEST(test_log_on_line_variadic)
TEST(test_log_on_line) TEST(test_log_on_line)
TEST(test_parser_alias_and_import_mix)
TEST(test_parser_alias_array) TEST(test_parser_alias_array)
TEST(test_parser_alias_simple) TEST(test_parser_alias_simple)
TEST(test_parser_bad_import_name) TEST(test_parser_bad_import_name)
@@ -211,6 +210,7 @@ static TestCase s_tests[] = {
TEST(test_parser_missing_semicolon_module) TEST(test_parser_missing_semicolon_module)
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_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)
+10 -8
View File
@@ -81,15 +81,17 @@ static void test_parser_alias_array(void) {
assert_true(valueType->builtin.isSigned, "expected signed"); assert_true(valueType->builtin.isSigned, "expected signed");
} }
static void test_parser_alias_and_import_mix(void) { static void test_parser_variable_simple(void) {
Module* m = test_get_ast(); Module* m = test_get_ast();
assert_not_null(m, "expected module to be parsed"); assert_not_null(m, "expected module to be parsed");
assert_int(2, (int)m->import_count, "expected 2 imports"); assert_int(1, (int)m->variable_count, "expected correct number of variables");
assert_int(2, (int)m->alias_count, "expected 2 aliases"); VariableDeclaration var = m->variables[0];
assert_str("my_var", var.name, "expected correct variable name");
assert_str("foo", m->imports[0].module_name, "expected import 1 name 'foo'"); assert_int(TYPE_EXPRESSION_BUILTIN, var.type.tag, "expected correct type tag");
assert_str("bar", m->imports[1].module_name, "expected import 2 name 'bar'"); assert_int(32, var.type.builtin.bitSize, "expected bitSize 32");
assert_str("myalias", m->aliases[0].name, "expected alias 1 name 'myalias'"); assert_true(var.type.builtin.isSigned, "expected signed");
assert_str("otheralias", m->aliases[1].name, "expected alias 2 name 'otheralias'"); assert_false(var.is_const, "expected not const");
assert_false(var.is_static, "expected not static");
} }
+4
View File
@@ -0,0 +1,4 @@
module my_module;
// Defines a global variable called my_var.
i32 my_var;