aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/ast.c1
-rw-r--r--src/ast.h5
-rw-r--r--src/parse/functions.c7
-rw-r--r--src/parse/types.c51
-rw-r--r--src/parse/types.h3
-rw-r--r--src/typecheck.c54
-rw-r--r--src/types.c18
7 files changed, 131 insertions, 8 deletions
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 <uniname.h>
#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;