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