aboutsummaryrefslogtreecommitdiff
path: root/src/parser.c
diff options
context:
space:
mode:
authorMustafa Quraish <[email protected]>2022-02-03 21:01:03 -0500
committerMustafa Quraish <[email protected]>2022-02-03 21:01:03 -0500
commit9f3edcbeea51dd8841b9d89fa7ef98f11682a1c7 (patch)
treeb2a4b6fa51ad558b36facde2cf952a6af5a25669 /src/parser.c
parentAdd automatic type inference for initialized variable declarations (diff)
downloadcup-9f3edcbeea51dd8841b9d89fa7ef98f11682a1c7.tar.xz
cup-9f3edcbeea51dd8841b9d89fa7ef98f11682a1c7.zip
Add support for basic structs
Structs for now (and probably for the near future) are not allowed to be passed by value, and instead you just pass a pointer to it. Nested structs can also be defined, and they can be either anonymous, or named (in which case only the members can access the type).
Diffstat (limited to 'src/parser.c')
-rw-r--r--src/parser.c109
1 files changed, 107 insertions, 2 deletions
diff --git a/src/parser.c b/src/parser.c
index 6bf2f1d..7c01c75 100644
--- a/src/parser.c
+++ b/src/parser.c
@@ -26,6 +26,11 @@ static i64 global_vars_offset = 0;
static Lexer *lexer_stack[LEXER_STACK_SIZE];
static i64 lexer_stack_count = 0;
+#define DEFINED_STRUCT_SIZE 128
+static Type *defined_structs[DEFINED_STRUCT_SIZE];
+static i64 defined_structs_count = 0;
+
+
Token do_assert_token(Token token, TokenType type, char *filename, int line)
{
if (token.type != type) {
@@ -43,6 +48,22 @@ Token do_assert_token(Token token, TokenType type, char *filename, int line)
* Some helpers
*/
+void push_struct_definition(Type *type)
+{
+ assert(defined_structs_count < DEFINED_STRUCT_SIZE);
+ defined_structs[defined_structs_count++] = type;
+}
+
+Type *find_custom_type_definition(Token *token)
+{
+ for (i64 i = 0; i < defined_structs_count; i++) {
+ if (strcmp(defined_structs[i]->struct_name, token->value.as_string) == 0) {
+ return defined_structs[i];
+ }
+ }
+ return NULL;
+}
+
void block_stack_push(Node *block)
{
assert(block_stack_count < BLOCK_STACK_SIZE);
@@ -164,7 +185,13 @@ Type *parse_type(Lexer *lexer)
Lexer_next(lexer);
type = type_new(TYPE_CHAR);
} else {
- die_location(token.loc, "Unexpected type found: %s", token_type_to_str(token.type));
+ assert_token(token, TOKEN_IDENTIFIER);
+ // TODO: Don't allow a type to contain itself.
+ // TODO: Don't allow a type to contain an array of itself.
+ type = find_custom_type_definition(&token);
+ if (!type)
+ die_location(token.loc, "Could not find what type `%s` is referencing", token.value.as_string);
+ Lexer_next(lexer);
}
for (;;) {
@@ -370,7 +397,7 @@ Node *parse_factor(Lexer *lexer)
expr = Node_new(OP_BWINV);
expr->unary_expr = parse_factor(lexer);
expr = handle_unary_expr_types(expr, &token);
-
+
// ++x is changed to (x = x + 1)
} else if (token.type == TOKEN_PLUSPLUS) {
Lexer_next(lexer);
@@ -454,6 +481,26 @@ Node *parse_factor(Lexer *lexer)
die_location(token.loc, "Post-incrementing is not supported\n");
} else if (token.type == TOKEN_MINUSMINUS) {
die_location(token.loc, "Post-decrementing is not supported\n");
+ } else if (token.type == TOKEN_DOT) {
+ // TODO: Pointer to struct
+ if (!is_struct_or_struct_ptr(expr->expr_type))
+ die_location(token.loc, "Cannot access member of non-struct type");
+
+ bool is_ptr = expr->expr_type->type == TYPE_PTR;
+ Type *struct_type = is_ptr ? expr->expr_type->ptr : expr->expr_type;
+ Lexer_next(lexer);
+ Token field_token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER);
+ i64 index = find_field_index(struct_type, field_token.value.as_string);
+ if (index == -1)
+ die_location(field_token.loc, "Struct `%s` does not have a field named `%s`", type_to_str(struct_type), field_token.value.as_string);
+
+ Node *member = Node_new(OP_MEMBER);
+ member->expr_type = struct_type->fields.type[index];
+ member->member.expr = expr;
+ member->member.offset = struct_type->fields.offset[index];
+ member->member.is_ptr = (expr->expr_type->type == TYPE_PTR);
+ expr = member;
+
} else {
break;
}
@@ -683,6 +730,9 @@ void parse_func_args(Lexer *lexer, Node *func)
assert_token(Lexer_next(lexer), TOKEN_COLON);
Type *type = parse_type(lexer);
+ if (type->type == TYPE_STRUCT)
+ die_location(token.loc, "Structs cannot be passed as arguments, maybe pass a pointer?");
+
i64 new_count = func->func.num_args + 1;
func->func.args = realloc(func->func.args, sizeof(Variable) * new_count);
Variable *var = &func->func.args[func->func.num_args++];
@@ -763,6 +813,57 @@ Lexer *remove_lexer()
return lexer_stack[lexer_stack_count - 1];
}
+Type *parse_struct_declaration(Lexer *lexer, bool is_global) {
+ i64 prev_struct_count = defined_structs_count;
+
+ assert_token(Lexer_next(lexer), TOKEN_STRUCT);
+
+ Type *struct_type = type_new(TYPE_STRUCT);
+
+ Token token = Lexer_peek(lexer);
+ // For nested temporary structs we don't need a name
+ if (token.type != TOKEN_IDENTIFIER && is_global)
+ die_location(token.loc, "You need to specify a name for the struct defined globally.");
+
+ // But if they do provide one, we'll add it to the list of defined structs so they
+ // it can referenced internally.
+ bool has_name = false;
+ if (token.type == TOKEN_IDENTIFIER) {
+ struct_type->struct_name = token.value.as_string;
+ push_struct_definition(struct_type);
+ Lexer_next(lexer);
+ has_name = true;
+ }
+
+ assert_token(Lexer_next(lexer), TOKEN_OPEN_BRACE);
+
+ token = Lexer_peek(lexer);
+ while (token.type != TOKEN_CLOSE_BRACE) {
+ token = assert_token(Lexer_next(lexer), TOKEN_IDENTIFIER);
+ assert_token(Lexer_next(lexer), TOKEN_COLON);
+
+ // We want to allow nested temporary structs.
+ Type *type;
+ Token next = Lexer_peek(lexer);
+ if (next.type == TOKEN_STRUCT) {
+ type = parse_struct_declaration(lexer, false);
+ } else {
+ type = parse_type(lexer);
+ }
+
+ push_field(struct_type, token.value.as_string, type);
+ assert_token(Lexer_next(lexer), TOKEN_SEMICOLON);
+ token = Lexer_peek(lexer);
+ }
+ assert_token(Lexer_next(lexer), TOKEN_CLOSE_BRACE);
+
+ // If this is not being defined globally, we want to remove it from the namespace.
+ if (!is_global)
+ defined_structs_count = prev_struct_count;
+
+ return struct_type;
+}
+
Node *parse_program(Lexer *lexer)
{
initialize_builtins();
@@ -778,6 +879,8 @@ Node *parse_program(Lexer *lexer)
} else if (token.type == TOKEN_LET) {
Node *var_decl = parse_var_declaration(lexer);
Node_add_child(program, var_decl);
+ } else if (token.type == TOKEN_STRUCT) {
+ parse_struct_declaration(lexer, true);
} else if (token.type == TOKEN_IMPORT) {
// TODO: Handle circular imports
// TODO: Handle complex import graphs (#pragma once)
@@ -788,6 +891,8 @@ Node *parse_program(Lexer *lexer)
char *filename = token.value.as_string;
lexer = Lexer_new_open_file(filename);
push_new_lexer(lexer);
+ } else if (token.type == TOKEN_SEMICOLON) {
+ Lexer_next(lexer);
} else {
die_location(token.loc, "Unexpected token in parse_program: `%s`\n", token_type_to_str(token.type));
exit(1);