Compare commits

..

2 Commits

Author SHA1 Message Date
seeseemelk 0704284726 Can parse variables 2026-04-29 21:39:48 +02:00
seeseemelk 94ae665a0a Add initial variable work 2026-04-29 21:20:52 +02:00
7 changed files with 136 additions and 18 deletions
+2 -3
View File
@@ -26,9 +26,8 @@ There will be no `test_buffer.h`. Instead, `test.c` will directly
Every syntax error path identified in the parser MUST have a corresponding test.
## Language Syntax
Since this is a compiler for a new language, do not assume anything
of its syntax.
Always check the `specs` directory.
Since this is a compiler for a new language, do not assume anything of its syntax.
Always check the `specs` directory to see examples and documentation about the language.
If there is anything unclear, ask the user for clarification.
It is certainly possible that there are contradictions in the
+2 -2
View File
@@ -6,10 +6,10 @@ Global variables can be defined as such:
```c2
// Defines a global variable called my_var.
int32 my_var;
i32 my_var;
// Defines a const variable.
const int32 my_var;
const i32 my_var;
// Defines a global variable whose type is determined automatically.
// The value will be determined at runtime.
+26
View File
@@ -62,6 +62,26 @@ typedef struct {
bool is_public;
} AliasDeclaration;
/**
* A declaration of a variable, which may be a constant or not, and may be static or not.
*/
typedef struct {
/** @brief The name of the variable. */
char* name;
/** @brief The type of the variable. */
TypeExpression type;
/** @brief Whether the variable is public or not. */
bool is_public;
/** @brief Whether the variable is static or not. */
bool is_static;
/** @brief Whether the variable is a constant or not. */
bool is_const;
} VariableDeclaration;
/**
* The top-level model.
* Every file matches an entire Module.
@@ -81,6 +101,12 @@ typedef struct {
/** @brief The number of aliases in the module. */
size_t alias_count;
/** @brief The list of variables in the module. */
VariableDeclaration* variables;
/** @brief The number of variables in the module. */
size_t variable_count;
} Module;
#endif
+90 -3
View File
@@ -264,6 +264,57 @@ static bool parse_alias_declaration(Parser* p, Module* module, bool is_public) {
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) {
Parser* p = malloc(sizeof(Parser));
p->ts = ts;
@@ -277,25 +328,53 @@ Module* parser_parse(TokenStream* ts) {
while (!parser_peek(p, TOKEN_EOF)) {
bool is_public = false;
bool is_static = false;
bool is_const = false;
bool terminal = false;
do {
while (!terminal) {
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)) {
goto fail;
}
terminal = true;
} 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)) {
goto fail;
}
terminal = true;
} else if (parser_accept(p, TOKEN_PUBLIC)) {
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 {
log_on_line(&p->token.location, "unexpected token");
goto fail;
}
} while (!terminal);
}
}
free(p);
@@ -333,6 +412,14 @@ void parser_free(Module* module) {
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);
free(module);
}
+1 -1
View File
@@ -201,7 +201,6 @@ static TestCase s_tests[] = {
TEST(test_log_error)
TEST(test_log_on_line_variadic)
TEST(test_log_on_line)
TEST(test_parser_alias_and_import_mix)
TEST(test_parser_alias_array)
TEST(test_parser_alias_simple)
TEST(test_parser_bad_import_name)
@@ -211,6 +210,7 @@ static TestCase s_tests[] = {
TEST(test_parser_missing_semicolon_module)
TEST(test_parser_module_name)
TEST(test_parser_public_imports)
TEST(test_parser_variable_simple)
TEST(test_tokenstream_comma)
TEST(test_tokenstream_info)
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");
}
static void test_parser_alias_and_import_mix(void) {
static void test_parser_variable_simple(void) {
Module* m = test_get_ast();
assert_not_null(m, "expected module to be parsed");
assert_int(2, (int)m->import_count, "expected 2 imports");
assert_int(2, (int)m->alias_count, "expected 2 aliases");
assert_str("foo", m->imports[0].module_name, "expected import 1 name 'foo'");
assert_str("bar", m->imports[1].module_name, "expected import 2 name 'bar'");
assert_str("myalias", m->aliases[0].name, "expected alias 1 name 'myalias'");
assert_str("otheralias", m->aliases[1].name, "expected alias 2 name 'otheralias'");
assert_int(1, (int)m->variable_count, "expected correct number of variables");
VariableDeclaration var = m->variables[0];
assert_str("my_var", var.name, "expected correct variable name");
assert_int(TYPE_EXPRESSION_BUILTIN, var.type.tag, "expected correct type tag");
assert_int(32, var.type.builtin.bitSize, "expected bitSize 32");
assert_true(var.type.builtin.isSigned, "expected signed");
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;