diff --git a/v0/ast.h b/v0/ast.h index 2e520b1..99f1ce6 100644 --- a/v0/ast.h +++ b/v0/ast.h @@ -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; diff --git a/v0/parser.c b/v0/parser.c index a93e4ed..2a46acf 100644 --- a/v0/parser.c +++ b/v0/parser.c @@ -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); } diff --git a/v0/test.c b/v0/test.c index 5c53f70..7fdcfb1 100644 --- a/v0/test.c +++ b/v0/test.c @@ -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) diff --git a/v0/test_parser.c b/v0/test_parser.c index ec950d2..5a5c0cd 100644 --- a/v0/test_parser.c +++ b/v0/test_parser.c @@ -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); +} diff --git a/v0/tests/parser_multiple_vars.c2 b/v0/tests/parser_multiple_vars.c2 new file mode 100644 index 0000000..6a1bd9c --- /dev/null +++ b/v0/tests/parser_multiple_vars.c2 @@ -0,0 +1,4 @@ +module test_multiple_vars; + +i32 var1; +i32 var2; diff --git a/v0/tests/parser_variable_const.c2 b/v0/tests/parser_variable_const.c2 new file mode 100644 index 0000000..cee8515 --- /dev/null +++ b/v0/tests/parser_variable_const.c2 @@ -0,0 +1,3 @@ +module test_const_var; + +const i32 my_const; diff --git a/v0/tests/parser_variable_init.c2 b/v0/tests/parser_variable_init.c2 new file mode 100644 index 0000000..561b3c5 --- /dev/null +++ b/v0/tests/parser_variable_init.c2 @@ -0,0 +1,2 @@ +module mymodule; +var x = 123; diff --git a/v0/tests/parser_variable_static.c2 b/v0/tests/parser_variable_static.c2 new file mode 100644 index 0000000..5692bc8 --- /dev/null +++ b/v0/tests/parser_variable_static.c2 @@ -0,0 +1,3 @@ +module test_static_var; + +static i32 my_static; diff --git a/v0/token.c b/v0/token.c index d4147d9..20039ac 100644 --- a/v0/token.c +++ b/v0/token.c @@ -44,6 +44,8 @@ static const KeywordMap keywords[] = { {"u16", TOKEN_U16}, {"u32", TOKEN_U32}, {"u64", TOKEN_U64}, + {"true", TOKEN_TRUE}, + {"false", TOKEN_FALSE}, }; /** @@ -234,8 +236,27 @@ 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 */ if (is_identifier_start(c)) { /* Declarations first for C89 */ diff --git a/v0/token.h b/v0/token.h index b067bb6..9167e5f 100644 --- a/v0/token.h +++ b/v0/token.h @@ -38,6 +38,10 @@ typedef enum { TOKEN_U16, TOKEN_U32, TOKEN_U64, + TOKEN_STRING, + TOKEN_INTEGER, + TOKEN_TRUE, + TOKEN_FALSE, /* Variable */ TOKEN_IDENTIFIER,