aboutsummaryrefslogtreecommitdiff
path: root/src/parse/types.c
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-08-25 00:55:29 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-08-25 00:55:29 -0400
commitc859ed479227cee2cecedb83d74a40acf9758051 (patch)
tree24b4e4e6d880999fab132a276d54e61f85308a74 /src/parse/types.c
parent75376335580c539512d459292d73e8ad4d7fe1af (diff)
Split out type parsing
Diffstat (limited to 'src/parse/types.c')
-rw-r--r--src/parse/types.c156
1 files changed, 156 insertions, 0 deletions
diff --git a/src/parse/types.c b/src/parse/types.c
new file mode 100644
index 00000000..36ddde45
--- /dev/null
+++ b/src/parse/types.c
@@ -0,0 +1,156 @@
+#include <stdarg.h>
+#include <stdbool.h>
+#include <string.h>
+
+#include <unictype.h>
+#include <uniname.h>
+
+#include "../ast.h"
+#include "../stdlib/print.h"
+#include "context.h"
+#include "errors.h"
+#include "functions.h"
+#include "parse.h"
+#include "types.h"
+#include "utils.h"
+
+type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ if (!match(&pos, "{")) return NULL;
+ whitespace(&pos);
+ type_ast_t *key_type = parse_type(ctx, pos);
+ if (!key_type) return NULL;
+ pos = key_type->end;
+ whitespace(&pos);
+ type_ast_t *value_type = NULL;
+ if (match(&pos, "=")) {
+ value_type = expect(ctx, start, &pos, parse_type, "I couldn't parse the rest of this table type");
+ } else {
+ return NULL;
+ }
+ spaces(&pos);
+ ast_t *default_value = NULL;
+ if (match(&pos, ";") && match_word(&pos, "default")) {
+ expect_str(ctx, pos, &pos, "=", "I expected an '=' here");
+ default_value =
+ expect(ctx, start, &pos, parse_extended_expr, "I couldn't parse the default value for this table");
+ }
+ whitespace(&pos);
+ expect_closing(ctx, &pos, "}", "I wasn't able to parse the rest of this table type");
+ return NewTypeAST(ctx->file, start, pos, TableTypeAST, .key = key_type, .value = value_type,
+ .default_value = default_value);
+}
+
+type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ if (!match(&pos, "|")) return NULL;
+ whitespace(&pos);
+ type_ast_t *item_type = parse_type(ctx, pos);
+ if (!item_type) return NULL;
+ pos = item_type->end;
+ whitespace(&pos);
+ expect_closing(ctx, &pos, "|", "I wasn't able to parse the rest of this set type");
+ return NewTypeAST(ctx->file, start, pos, SetTypeAST, .item = item_type);
+}
+
+type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ if (!match_word(&pos, "func")) return NULL;
+ spaces(&pos);
+ expect_str(ctx, start, &pos, "(", "I expected a parenthesis here");
+ arg_ast_t *args = parse_args(ctx, &pos);
+ spaces(&pos);
+ type_ast_t *ret = match(&pos, "->") ? optional(ctx, &pos, parse_type) : NULL;
+ expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this function type");
+ return NewTypeAST(ctx->file, start, pos, FunctionTypeAST, .args = args, .ret = ret);
+}
+
+type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ if (!match(&pos, "[")) return NULL;
+ type_ast_t *type = expect(ctx, start, &pos, parse_type, "I couldn't parse a list item type after this point");
+ expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this list type");
+ return NewTypeAST(ctx->file, start, pos, ListTypeAST, .item = type);
+}
+
+type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ bool is_stack;
+ if (match(&pos, "@")) is_stack = false;
+ else if (match(&pos, "&")) is_stack = true;
+ else return NULL;
+
+ spaces(&pos);
+ type_ast_t *type =
+ expect(ctx, start, &pos, parse_non_optional_type, "I couldn't parse a pointer type after this point");
+ type_ast_t *ptr_type = NewTypeAST(ctx->file, start, pos, PointerTypeAST, .pointed = type, .is_stack = is_stack);
+ spaces(&pos);
+ while (match(&pos, "?"))
+ ptr_type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type = ptr_type);
+ return ptr_type;
+}
+
+type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ const char *id = get_id(&pos);
+ if (!id) return NULL;
+ for (;;) {
+ const char *next = pos;
+ spaces(&next);
+ if (!match(&next, ".")) break;
+ const char *next_id = get_id(&next);
+ if (!next_id) break;
+ id = String(id, ".", next_id);
+ pos = next;
+ }
+ return NewTypeAST(ctx->file, start, pos, VarTypeAST, .name = id);
+}
+
+type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ type_ast_t *type = NULL;
+ bool success = (false || (type = parse_pointer_type(ctx, pos)) || (type = parse_list_type(ctx, pos))
+ || (type = parse_table_type(ctx, pos)) || (type = parse_set_type(ctx, pos))
+ || (type = parse_type_name(ctx, pos)) || (type = parse_func_type(ctx, pos)));
+ if (!success && match(&pos, "(")) {
+ whitespace(&pos);
+ type = optional(ctx, &pos, parse_type);
+ if (!type) return NULL;
+ whitespace(&pos);
+ expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this type");
+ type->start = start;
+ type->end = pos;
+ }
+
+ return type;
+}
+
+type_ast_t *parse_type(parse_ctx_t *ctx, const char *pos) {
+ const char *start = pos;
+ type_ast_t *type = parse_non_optional_type(ctx, pos);
+ if (!type) return NULL;
+ pos = type->end;
+ spaces(&pos);
+ while (match(&pos, "?"))
+ type = NewTypeAST(ctx->file, start, pos, OptionalTypeAST, .type = type);
+ return type;
+}
+
+type_ast_t *parse_type_str(const char *str) {
+ file_t *file = spoof_file("<type>", str);
+ parse_ctx_t ctx = {
+ .file = file,
+ .on_err = NULL,
+ };
+
+ const char *pos = file->text;
+ whitespace(&pos);
+ type_ast_t *ast = parse_type(&ctx, pos);
+ if (!ast) return ast;
+ pos = ast->end;
+ whitespace(&pos);
+ if (strlen(pos) > 0) {
+ parser_err(&ctx, pos, pos + strlen(pos), "I couldn't parse this part of the type");
+ }
+ return ast;
+}