More variable stuff
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
+39
-9
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
module test_multiple_vars;
|
||||
|
||||
i32 var1;
|
||||
i32 var2;
|
||||
@@ -0,0 +1,3 @@
|
||||
module test_const_var;
|
||||
|
||||
const i32 my_const;
|
||||
@@ -0,0 +1,2 @@
|
||||
module mymodule;
|
||||
var x = 123;
|
||||
@@ -0,0 +1,3 @@
|
||||
module test_static_var;
|
||||
|
||||
static i32 my_static;
|
||||
+21
@@ -44,6 +44,8 @@ static const KeywordMap keywords[] = {
|
||||
{"u16", TOKEN_U16},
|
||||
{"u32", TOKEN_U32},
|
||||
{"u64", TOKEN_U64},
|
||||
{"true", TOKEN_TRUE},
|
||||
{"false", TOKEN_FALSE},
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -234,6 +236,25 @@ 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 */
|
||||
|
||||
@@ -38,6 +38,10 @@ typedef enum {
|
||||
TOKEN_U16,
|
||||
TOKEN_U32,
|
||||
TOKEN_U64,
|
||||
TOKEN_STRING,
|
||||
TOKEN_INTEGER,
|
||||
TOKEN_TRUE,
|
||||
TOKEN_FALSE,
|
||||
|
||||
/* Variable */
|
||||
TOKEN_IDENTIFIER,
|
||||
|
||||
Reference in New Issue
Block a user