Can parse variables
This commit is contained in:
+2
-2
@@ -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.
|
||||
|
||||
+90
-3
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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");
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module my_module;
|
||||
|
||||
// Defines a global variable called my_var.
|
||||
i32 my_var;
|
||||
Reference in New Issue
Block a user