More variable stuff
This commit is contained in:
@@ -17,6 +17,21 @@ typedef struct {
|
|||||||
bool is_public;
|
bool is_public;
|
||||||
} ImportDeclaration;
|
} 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 {
|
typedef enum {
|
||||||
TYPE_EXPRESSION_BUILTIN,
|
TYPE_EXPRESSION_BUILTIN,
|
||||||
TYPE_EXPRESSION_ARRAY
|
TYPE_EXPRESSION_ARRAY
|
||||||
@@ -26,7 +41,7 @@ typedef enum {
|
|||||||
* An expression that evaluates to a type.
|
* An expression that evaluates to a type.
|
||||||
*/
|
*/
|
||||||
typedef struct TypeExpression TypeExpression;
|
typedef struct TypeExpression TypeExpression;
|
||||||
struct TypeExpression{
|
struct TypeExpression {
|
||||||
/** @brief defines which entry in the union is valid */
|
/** @brief defines which entry in the union is valid */
|
||||||
TypeExpressionTag tag;
|
TypeExpressionTag tag;
|
||||||
union {
|
union {
|
||||||
@@ -53,13 +68,10 @@ struct TypeExpression{
|
|||||||
*/
|
*/
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/** @brief The name of the alias. */
|
/** @brief The name of the alias. */
|
||||||
char* name;
|
const char* name;
|
||||||
|
|
||||||
/** @brief The value of the alias. */
|
/** @brief The value of the alias. */
|
||||||
TypeExpression value;
|
TypeExpression value;
|
||||||
|
|
||||||
/** @brief Whether the import is public or not. */
|
|
||||||
bool is_public;
|
|
||||||
} AliasDeclaration;
|
} AliasDeclaration;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -72,6 +84,9 @@ typedef struct {
|
|||||||
/** @brief The type of the variable. */
|
/** @brief The type of the variable. */
|
||||||
TypeExpression type;
|
TypeExpression type;
|
||||||
|
|
||||||
|
/** @brief The optional initializer expression. */
|
||||||
|
Expression* initializer;
|
||||||
|
|
||||||
/** @brief Whether the variable is public or not. */
|
/** @brief Whether the variable is public or not. */
|
||||||
bool is_public;
|
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];
|
AliasDeclaration* alias = &module->aliases[module->alias_count - 1];
|
||||||
memset(alias, 0, sizeof(AliasDeclaration));
|
memset(alias, 0, sizeof(AliasDeclaration));
|
||||||
alias->is_public = is_public;
|
|
||||||
|
|
||||||
if (!parser_require(p, TOKEN_IDENTIFIER, "expected alias identifier")) {
|
if (!parser_require(p, TOKEN_IDENTIFIER, "expected alias identifier")) {
|
||||||
return false;
|
return false;
|
||||||
@@ -264,15 +263,33 @@ static bool parse_alias_declaration(Parser* p, Module* module, bool is_public) {
|
|||||||
return true;
|
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.
|
* 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) {
|
static bool parse_variable_declaration(Parser* p, Module* module, bool is_public, bool is_static, bool is_const) {
|
||||||
module->variable_count++;
|
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);
|
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")) {
|
if (!parser_expect(p, TOKEN_SEMICOLON, "expected ';' after variable declaration")) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -406,7 +430,7 @@ void parser_free(Module* module) {
|
|||||||
|
|
||||||
if (module->aliases != NULL) {
|
if (module->aliases != NULL) {
|
||||||
for(size_t i = 0; i < module->alias_count; i++) {
|
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_type_expression(&module->aliases[i].value);
|
||||||
}
|
}
|
||||||
free(module->aliases);
|
free(module->aliases);
|
||||||
@@ -416,6 +440,12 @@ void parser_free(Module* module) {
|
|||||||
for(size_t i = 0; i < module->variable_count; i++) {
|
for(size_t i = 0; i < module->variable_count; i++) {
|
||||||
free(module->variables[i].name);
|
free(module->variables[i].name);
|
||||||
free_type_expression(&module->variables[i].type);
|
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);
|
free(module->variables);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -211,6 +211,10 @@ static TestCase s_tests[] = {
|
|||||||
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_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_comma)
|
||||||
TEST(test_tokenstream_info)
|
TEST(test_tokenstream_info)
|
||||||
TEST(test_tokenstream_keywords_and_symbols)
|
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");
|
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},
|
{"u16", TOKEN_U16},
|
||||||
{"u32", TOKEN_U32},
|
{"u32", TOKEN_U32},
|
||||||
{"u64", TOKEN_U64},
|
{"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_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_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 '=': 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 */
|
/* Keywords and identifiers */
|
||||||
if (is_identifier_start(c)) {
|
if (is_identifier_start(c)) {
|
||||||
/* Declarations first for C89 */
|
/* Declarations first for C89 */
|
||||||
|
|||||||
@@ -38,6 +38,10 @@ typedef enum {
|
|||||||
TOKEN_U16,
|
TOKEN_U16,
|
||||||
TOKEN_U32,
|
TOKEN_U32,
|
||||||
TOKEN_U64,
|
TOKEN_U64,
|
||||||
|
TOKEN_STRING,
|
||||||
|
TOKEN_INTEGER,
|
||||||
|
TOKEN_TRUE,
|
||||||
|
TOKEN_FALSE,
|
||||||
|
|
||||||
/* Variable */
|
/* Variable */
|
||||||
TOKEN_IDENTIFIER,
|
TOKEN_IDENTIFIER,
|
||||||
|
|||||||
Reference in New Issue
Block a user