aboutsummaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
authorMustafa Quraish <[email protected]>2022-01-31 21:20:16 -0500
committerMustafa Quraish <[email protected]>2022-01-31 21:20:16 -0500
commit18aafac383e3180a8d57e1ed88470d94b1477a06 (patch)
tree4212ed36cd4d2a3b28603147a5d037674126cbaf /src/parser.c
parentAdd .gitattributes to try to highlight code as Rust (diff)
downloadcup-18aafac383e3180a8d57e1ed88470d94b1477a06.tar.xz
cup-18aafac383e3180a8d57e1ed88470d94b1477a06.zip
Global variables now supported! + some fixes to OP_ASSIGN
Previously we weren't creating a new assignment node, and this was causing all sorts of funky errors. This commit also fixes that, and we can now use global variables :^)
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c72
1 files changed, 60 insertions, 12 deletions
diff --git a/src/parser.c b/src/parser.c
index 4dc891d..a0ec7a1 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -15,11 +15,16 @@ static Node *block_stack[BLOCK_STACK_SIZE];
static i64 block_stack_count = 0;
static i64 cur_stack_offset = 0;
+// TODO: Probably use a vector here
+#define GLOBAL_VARS_SIZE 1024
+static Variable *global_vars[GLOBAL_VARS_SIZE];
+static i64 global_vars_count = 0;
+static i64 global_vars_offset = 0;
+
#define LEXER_STACK_SIZE 64
static Lexer *lexer_stack[LEXER_STACK_SIZE];
static i64 lexer_stack_count = 0;
-
Token do_assert_token(Token token, TokenType type, char *filename, int line)
{
if (token.type != type) {
@@ -133,6 +138,9 @@ Node *find_builtin_function(Token *token)
Variable *find_local_variable(Token *token)
{
+ if (current_function == NULL)
+ return NULL;
+
assert_token(*token, TOKEN_IDENTIFIER);
for (i64 i = block_stack_count - 1; i >= 0; --i) {
Node *block = block_stack[i];
@@ -151,6 +159,16 @@ Variable *find_local_variable(Token *token)
return NULL;
}
+Variable *find_global_variable(Token *token)
+{
+ for (int i = 0; i < global_vars_count; i++) {
+ if (strcmp(global_vars[i]->name, token->value.as_string) == 0) {
+ return global_vars[i];
+ }
+ }
+ return NULL;
+}
+
Node *find_function_definition(Token *token)
{
assert_token(*token, TOKEN_IDENTIFIER);
@@ -163,6 +181,15 @@ Node *find_function_definition(Token *token)
return NULL;
}
+void add_global_variable(Variable *var)
+{
+ var->offset = global_vars_offset;
+ // TODO: Compute based on type
+ int var_size = 8;
+ global_vars_offset += var_size;
+ global_vars[global_vars_count++] = var;
+}
+
// TODO: rename this, it's ugly
void add_variable_to_current_block(Variable *var)
{
@@ -224,16 +251,16 @@ Node *parse_expression(Lexer *);
Node *parse_var_declaration(Lexer *lexer)
{
+ bool is_global = (current_function == NULL);
Token token = assert_token(Lexer_next(lexer), TOKEN_LET);
- // TODO: Reuse this for globals? Or maybe just make a new function?
- if (!current_function || current_function->type != AST_FUNC)
- die_location(token.loc, "Variable declaration outside of function");
token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER);
// NOTE: We don't allow shadowing of variables in the any blocks,
// this is by design since it's a common mistake.
if (find_local_variable(&token) != NULL)
- die_location(token.loc, "Variable `%s` already declared", token.value.as_string);
+ die_location(token.loc, "Variable `%s` already declared in function", token.value.as_string);
+ if (find_global_variable(&token) != NULL)
+ die_location(token.loc, "Variable `%s` already declared globally", token.value.as_string);
Node *node = Node_new(AST_VARDECL);
node->var_decl.var.name = token.value.as_string;
@@ -243,10 +270,16 @@ Node *parse_var_declaration(Lexer *lexer)
die_location(token.loc, "Missing type specifier for variable `%s`", node->var_decl.var.name);
node->var_decl.var.type = parse_type(lexer);
- add_variable_to_current_block(&node->var_decl.var);
+ if (is_global) {
+ add_global_variable(&node->var_decl.var);
+ } else {
+ add_variable_to_current_block(&node->var_decl.var);
+ }
token = Lexer_next(lexer);
if (token.type == TOKEN_ASSIGN) {
+ if (is_global)
+ die_location(token.loc, "Cannot initialize global variable `%s` outside function", node->var_decl.var.name);
node->var_decl.value = parse_expression(lexer);
assert_token(Lexer_next(lexer), TOKEN_SEMICOLON);
} else {
@@ -317,11 +350,19 @@ Node *parse_factor(Lexer *lexer)
Variable *var = find_local_variable(&token);
if (var != NULL) {
Lexer_next(lexer);
- expr = Node_new(AST_VAR);
+ expr = Node_new(AST_LOCAL_VAR);
expr->variable = var;
return expr;
}
+ Variable *gvar = find_global_variable(&token);
+ if (gvar != NULL) {
+ Lexer_next(lexer);
+ expr = Node_new(AST_GLOBAL_VAR);
+ expr->variable = gvar;
+ return expr;
+ }
+
Node *func = find_function_definition(&token);
if (func != NULL) {
return parse_function_call_args(lexer, func);
@@ -396,14 +437,14 @@ Node *parse_expression(Lexer *lexer)
Node *node = parse_conditional_exp(lexer);
// FIXME: This is a hack to handle assignment expressions
// and can probably be done properly.
- if (node->type == AST_VAR) {
+ if (node->type == AST_LOCAL_VAR || node->type == AST_GLOBAL_VAR) {
Token token = Lexer_peek(lexer);
if (token.type == TOKEN_ASSIGN) {
Lexer_next(lexer);
- Variable *var = node->variable;
- node->type = OP_ASSIGN;
- node->assign.var = var;
- node->assign.value = parse_expression(lexer);
+ Node *assign = Node_new(OP_ASSIGN);
+ assign->assign.var = node;
+ assign->assign.value = parse_expression(lexer);
+ node = assign;
}
}
return node;
@@ -577,6 +618,9 @@ Node *parse_func(Lexer *lexer)
assert(block_stack_count == 0);
assert(cur_stack_offset == 0);
+ // Reset current function
+ current_function = NULL;
+
return func;
}
@@ -607,6 +651,9 @@ Node *parse_program(Lexer *lexer)
if (token.type == TOKEN_FN) {
Node *func = parse_func(lexer);
Node_add_child(program, func);
+ } else if (token.type == TOKEN_LET) {
+ Node *var_decl = parse_var_declaration(lexer);
+ Node_add_child(program, var_decl);
} else if (token.type == TOKEN_IMPORT) {
// TODO: Handle circular imports
// TODO: Handle complex import graphs (#pragma once)
@@ -629,5 +676,6 @@ Node *parse_program(Lexer *lexer)
token = Lexer_peek(lexer);
}
}
+ program->block.locals_size = global_vars_offset;
return program;
} \ No newline at end of file