From b9a8ddea2c0b5f170e461b9216eb14f2d86588eb Mon Sep 17 00:00:00 2001 From: Bruce Hill Date: Sat, 20 Sep 2025 14:07:26 -0400 Subject: WIP on inline enums --- src/ast.c | 1 + src/ast.h | 5 +++++ src/parse/functions.c | 7 +++---- src/parse/types.c | 51 +++++++++++++++++++++++++++++++++++++++++++++++- src/parse/types.h | 3 ++- src/typecheck.c | 54 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/types.c | 18 +++++++++++++++-- 7 files changed, 131 insertions(+), 8 deletions(-) (limited to 'src') diff --git a/src/ast.c b/src/ast.c index 37c5a514..d59058c9 100644 --- a/src/ast.c +++ b/src/ast.c @@ -149,6 +149,7 @@ Text_t type_ast_to_sexp(type_ast_t *t) { T(TableTypeAST, "(TableType ", type_ast_to_sexp(data.key), " ", type_ast_to_sexp(data.value), ")"); T(FunctionTypeAST, "(FunctionType ", arg_defs_to_sexp(data.args), " ", type_ast_to_sexp(data.ret), ")"); T(OptionalTypeAST, "(OptionalType ", type_ast_to_sexp(data.type), ")"); + T(EnumTypeAST, "(EnumType ", data.name, " ", tags_to_sexp(data.tags), ")"); #undef T default: return EMPTY_TEXT; } diff --git a/src/ast.h b/src/ast.h index 62dbff0e..2e562514 100644 --- a/src/ast.h +++ b/src/ast.h @@ -87,6 +87,7 @@ typedef enum { TableTypeAST, FunctionTypeAST, OptionalTypeAST, + EnumTypeAST, } type_ast_e; typedef struct tag_ast_s { @@ -128,6 +129,10 @@ struct type_ast_s { struct { type_ast_t *type; } OptionalTypeAST; + struct { + Text_t name; + tag_ast_t *tags; + } EnumTypeAST; } __data; }; diff --git a/src/parse/functions.c b/src/parse/functions.c index ceb0a8bc..ea20e385 100644 --- a/src/parse/functions.c +++ b/src/parse/functions.c @@ -47,15 +47,14 @@ arg_ast_t *parse_args(parse_ctx_t *ctx, const char **pos) { } if (match(pos, ":")) { - type = expect(ctx, *pos - 1, pos, parse_type, "I expected a type here"); + type = expect(ctx, *pos, pos, parse_type, "I expected a type here"); whitespace(ctx, pos); - if (match(pos, "=")) - default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); + if (match(pos, "=")) default_val = expect(ctx, *pos, pos, parse_term, "I expected a value here"); names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names); break; } else if (strncmp(*pos, "==", 2) != 0 && match(pos, "=")) { - default_val = expect(ctx, *pos - 1, pos, parse_term, "I expected a value after this '='"); + default_val = expect(ctx, *pos, pos, parse_term, "I expected a value here"); names = new (name_list_t, .start = name_start, .end = *pos, .name = name, .alias = alias, .next = names); break; diff --git a/src/parse/types.c b/src/parse/types.c index ffb7d869..683e51ce 100644 --- a/src/parse/types.c +++ b/src/parse/types.c @@ -6,7 +6,9 @@ #include #include "../ast.h" +#include "../stdlib/datatypes.h" #include "../stdlib/print.h" +#include "../stdlib/text.h" #include "context.h" #include "errors.h" #include "expressions.h" @@ -90,6 +92,52 @@ type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos) { return ptr_type; } +type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos) { + // tagged union: enum(A, B(x:Int,y:Int)=5, ...) + const char *start = pos; + if (!match_word(&pos, "enum")) return NULL; + spaces(&pos); + if (!match(&pos, "(")) return NULL; + + tag_ast_t *tags = NULL; + whitespace(ctx, &pos); + for (;;) { + spaces(&pos); + const char *tag_start = pos; + const char *tag_name = get_id(&pos); + if (!tag_name) break; + + spaces(&pos); + arg_ast_t *fields; + bool secret = false; + if (match(&pos, "(")) { + whitespace(ctx, &pos); + fields = parse_args(ctx, &pos); + whitespace(ctx, &pos); + if (match(&pos, ";")) { // Extra flags + whitespace(ctx, &pos); + secret = match_word(&pos, "secret"); + whitespace(ctx, &pos); + } + expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this tagged union member"); + } else { + fields = NULL; + } + + tags = new (tag_ast_t, .start = tag_start, .end = pos, .name = tag_name, .fields = fields, .secret = secret, + .next = tags); + + if (!match_separator(ctx, &pos)) break; + } + + whitespace(ctx, &pos); + expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this enum definition"); + + REVERSE_LIST(tags); + Text_t name = Texts("enum$", (int64_t)(start - ctx->file->text)); + return NewTypeAST(ctx->file, start, pos, EnumTypeAST, .name = name, .tags = tags); +} + type_ast_t *parse_type_name(parse_ctx_t *ctx, const char *pos) { const char *start = pos; const char *id = get_id(&pos); @@ -111,7 +159,8 @@ type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *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))); + || (type = parse_enum_type(ctx, pos)) || (type = parse_type_name(ctx, pos)) + || (type = parse_func_type(ctx, pos))); if (!success && match(&pos, "(")) { whitespace(ctx, &pos); type = optional(ctx, &pos, parse_type); diff --git a/src/parse/types.h b/src/parse/types.h index 186d5322..ac6c22fb 100644 --- a/src/parse/types.h +++ b/src/parse/types.h @@ -6,8 +6,9 @@ type_ast_t *parse_type_str(const char *str); -type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos); +type_ast_t *parse_enum_type(parse_ctx_t *ctx, const char *pos); type_ast_t *parse_func_type(parse_ctx_t *ctx, const char *pos); +type_ast_t *parse_list_type(parse_ctx_t *ctx, const char *pos); type_ast_t *parse_non_optional_type(parse_ctx_t *ctx, const char *pos); type_ast_t *parse_pointer_type(parse_ctx_t *ctx, const char *pos); type_ast_t *parse_set_type(parse_ctx_t *ctx, const char *pos); diff --git a/src/typecheck.c b/src/typecheck.c index e9968f09..86876c30 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -121,6 +121,60 @@ type_t *parse_type_ast(env_t *env, type_ast_t *ast) { else if (t->tag == OptionalType) code_err(ast, "Nested optional types are not currently supported"); return Type(OptionalType, .type = t); } + case EnumTypeAST: { + tag_ast_t *tag_asts = Match(ast, EnumTypeAST)->tags; + tag_t *tags = NULL; + int64_t tag_value = 1; + const char *enum_name = Text$as_c_string(Match(ast, EnumTypeAST)->name); + env_t *ns_env = namespace_env(env, enum_name); + + type_t *enum_type = Type(EnumType, .name = enum_name, .env = ns_env, .tags = NULL); + + Table$str_set(env->types, enum_name, enum_type); + + bool has_any_tags_with_fields = false; + for (tag_ast_t *tag_ast = tag_asts; tag_ast; tag_ast = tag_ast->next) { + has_any_tags_with_fields = has_any_tags_with_fields || tag_ast->fields; + } + + for (tag_ast_t *tag_ast = tag_asts; tag_ast; tag_ast = tag_ast->next) { + arg_t *fields = NULL; + for (arg_ast_t *field_ast = tag_ast->fields; field_ast; field_ast = field_ast->next) { + type_t *field_type = + field_ast->type ? parse_type_ast(env, field_ast->type) : get_type(env, field_ast->value); + fields = new (arg_t, .name = field_ast->name, .type = field_type, .default_val = field_ast->value, + .next = fields); + } + REVERSE_LIST(fields); + const char *struct_name = String(enum_name, "$$", tag_ast->name); + env_t *struct_env = namespace_env(env, struct_name); + type_t *tag_type = Type(StructType, .name = tag_ast->name, .fields = fields, .env = struct_env); + tags = new (tag_t, .name = tag_ast->name, .tag_value = tag_value, .type = tag_type, .next = tags); + + if (Match(tag_type, StructType)->fields) { // Constructor: + type_t *constructor_t = + Type(FunctionType, .args = Match(tag_type, StructType)->fields, .ret = enum_type); + Text_t tagged_name = namespace_name(env, env->namespace, Texts(enum_name, "$tagged$", tag_ast->name)); + set_binding(ns_env, tag_ast->name, constructor_t, tagged_name); + binding_t binding = {.type = constructor_t, .code = tagged_name}; + List$insert(&ns_env->namespace->constructors, &binding, I(1), sizeof(binding)); + } else if (has_any_tags_with_fields) { // Empty singleton value: + Text_t code = + Texts("((", namespace_name(env, env->namespace, Texts(enum_name, "$$type")), "){", + namespace_name(env, env->namespace, Texts(enum_name, "$tag$", tag_ast->name)), "})"); + set_binding(ns_env, tag_ast->name, enum_type, code); + } else { + Text_t code = namespace_name(env, env->namespace, Texts(enum_name, "$tag$", tag_ast->name)); + set_binding(ns_env, tag_ast->name, enum_type, code); + } + Table$str_set(env->types, String(enum_name, "$", tag_ast->name), tag_type); + + tag_value += 1; + } + REVERSE_LIST(tags); + enum_type->__data.EnumType.tags = tags; + return enum_type; + } case UnknownTypeAST: code_err(ast, "I don't know how to get this type"); } #ifdef __GNUC__ diff --git a/src/types.c b/src/types.c index b0caca1a..a670f212 100644 --- a/src/types.c +++ b/src/types.c @@ -67,8 +67,22 @@ Text_t type_to_text(type_t *t) { return Texts(sigil, type_to_text(ptr->pointed)); } case EnumType: { - DeclareMatch(tagged, t, EnumType); - return Text$from_str(tagged->name); + DeclareMatch(enum_, t, EnumType); + return Text$from_str(enum_->name); + // Text_t text = Text("enum("); + // for (tag_t *tag = enum_->tags; tag; tag = tag->next) { + // text = Texts(text, Text$from_str(tag->name)); + // if (tag->type && Match(tag->type, StructType)->fields) { + // text = Texts(text, "("); + // for (arg_t *field = Match(tag->type, StructType)->fields; field; field = field->next) { + // text = Texts(text, Text$from_str(field->name), ":", type_to_text(field->type)); + // if (field->next) text = Texts(text, ", "); + // } + // text = Texts(text, ")"); + // } + // if (tag->next) text = Texts(text, ", "); + // } + // return enum_->name ? Text$from_str(enum_->name) : Text("enum"); } case OptionalType: { type_t *opt = Match(t, OptionalType)->type; -- cgit v1.2.3