diff --git a/Makefile b/Makefile index 5aba397..f5682ee 100644 --- a/Makefile +++ b/Makefile @@ -28,7 +28,7 @@ BUILTIN_OBJS=builtins/array.o builtins/bool.o builtins/color.o builtins/nums.o b all: libnext.so nextlang -nextlang: nextlang.c SipHash/halfsiphash.o util.o files.o ast.o parse.o environment.o types.o typecheck.o structs.o compile.o $(BUILTIN_OBJS) +nextlang: nextlang.c SipHash/halfsiphash.o util.o files.o ast.o parse.o environment.o types.o typecheck.o structs.o enums.o compile.o $(BUILTIN_OBJS) libnext.so: util.o files.o $(BUILTIN_OBJS) SipHash/halfsiphash.o $(CC) $^ $(CFLAGS) $(EXTRA) $(CWARN) $(G) $(O) $(OSFLAGS) $(LDLIBS) -Wl,-soname,libnext.so -shared -o $@ diff --git a/ast.h b/ast.h index 4200fc0..254ac53 100644 --- a/ast.h +++ b/ast.h @@ -57,6 +57,7 @@ typedef struct tag_ast_s { const char *name; arg_ast_t *fields; int64_t value; + bool secret:1; struct tag_ast_s *next; } tag_ast_t; diff --git a/compile.c b/compile.c index 651312c..8679934 100644 --- a/compile.c +++ b/compile.c @@ -7,6 +7,7 @@ #include "ast.h" #include "builtins/string.h" #include "compile.h" +#include "enums.h" #include "structs.h" #include "environment.h" #include "typecheck.h" @@ -771,48 +772,7 @@ CORD compile(env_t *env, ast_t *ast) return CORD_EMPTY; } case EnumDef: { - auto def = Match(ast, EnumDef); - CORD_appendf(&env->code->typedefs, "typedef struct %s_s %s_t;\n", def->name, def->name); - CORD enum_def = CORD_all("struct ", def->name, "_s {\n" - "\tenum {"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - CORD_appendf(&enum_def, "$tag$%s$%s = %ld", def->name, tag->name, tag->value); - if (tag->next) enum_def = CORD_cat(enum_def, ", "); - } - enum_def = CORD_cat(enum_def, "} $tag;\n" - "union {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - (void)compile(env, WrapAST(ast, StructDef, .name=heap_strf("%s$%s", def->name, tag->name), .fields=tag->fields)); - enum_def = CORD_all(enum_def, def->name, "$", tag->name, "_t ", tag->name, ";\n"); - CORD_appendf(&env->code->typedefs, "#define %s__%s(...) ((%s_t){$tag$%s$%s, .%s={__VA_ARGS__}})\n", - def->name, tag->name, def->name, def->name, tag->name, tag->name); - } - enum_def = CORD_cat(enum_def, "};\n};\n"); - env->code->typecode = CORD_cat(env->code->typecode, enum_def); - - CORD cord_func = CORD_all("static CORD ", def->name, "__as_str(", def->name, "_t *obj, bool use_color) {\n", - "\tif (!obj) return \"", def->name, "\";\n", - "\tswitch (obj->$tag) {\n"); - for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { - cord_func = CORD_all(cord_func, "\tcase $tag$", def->name, "$", tag->name, ": return CORD_all(use_color ? \"\\x1b[36;1m", - def->name, ".", tag->name, "\\x1b[m(\" : \"", def->name, ".", tag->name, "(\""); - - for (arg_ast_t *field = tag->fields; field; field = field->next) { - type_t *field_t = parse_type_ast(env, field->type); - CORD field_str = expr_as_string(env, CORD_all("obj->", tag->name, ".", field->name), field_t, "use_color"); - CORD_appendf(&cord_func, ", \"%s=\", %r", field->name, field_str); - if (field->next) CORD_appendf(&cord_func, ", \", \""); - } - CORD_appendf(&cord_func, ", \")\");\n"); - } - CORD_appendf(&cord_func, "\t}\n}\n"); - env->code->funcs = CORD_cat(env->code->funcs, cord_func); - - // Typeinfo: - CORD_appendf(&env->code->typedefs, "typedef struct { TypeInfo type; } %s_namespace_t;\n", def->name); - CORD_appendf(&env->code->typedefs, "extern %s_namespace_t %s;\n", def->name, def->name); - CORD_appendf(&env->code->typeinfos, "public %s_namespace_t %s = {{.tag=CustomInfo, .CustomInfo={.as_str=(void*)%s__as_str}}};\n", def->name, def->name, def->name); - + compile_enum_def(env, ast); return CORD_EMPTY; } case DocTest: { diff --git a/enums.c b/enums.c new file mode 100644 index 0000000..890bf55 --- /dev/null +++ b/enums.c @@ -0,0 +1,136 @@ + +#include +#include +#include +#include + +#include "ast.h" +#include "builtins/string.h" +#include "compile.h" +#include "structs.h" +#include "environment.h" +#include "typecheck.h" +#include "util.h" + +static CORD compile_str_method(env_t *env, ast_t *ast) +{ + auto def = Match(ast, EnumDef); + CORD str_func = CORD_all("static CORD ", def->name, "__as_str(", def->name, "_t *obj, bool use_color) {\n" + "\tif (!obj) return \"", def->name, "\";\n" + "switch (obj->$tag) {\n"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + str_func = CORD_all(str_func, "\tcase $tag$", def->name, "$", tag->name, ": return CORD_all(use_color ? \"\\x1b[36;1m", + def->name, ".", tag->name, "\\x1b[m(\" : \"", def->name, ".", tag->name, "(\""); + + if (tag->secret) { + str_func = CORD_cat(str_func, ", \"***)\");\n"); + continue; + } + + for (arg_ast_t *field = tag->fields; field; field = field->next) { + type_t *field_t = parse_type_ast(env, field->type); + CORD field_str = expr_as_string(env, CORD_all("obj->", tag->name, ".", field->name), field_t, "use_color"); + str_func = CORD_all(str_func, ", \"", field->name, "=\", ", field_str); + if (field->next) str_func = CORD_cat(str_func, ", \", \""); + } + str_func = CORD_cat(str_func, ", \")\");\n"); + } + str_func = CORD_cat(str_func, "\t}\n}\n"); + return str_func; +} + +static CORD compile_compare_method(env_t *env, ast_t *ast) +{ + auto def = Match(ast, EnumDef); + CORD cmp_func = CORD_all("static int ", def->name, "__compare(const ", def->name, "_t *x, const ", def->name, + "_t *y, const TypeInfo *info) {\n" + "int diff = x->$tag - y->$tag;\n" + "if (diff) return diff;\n" + "switch (x->$tag) {\n"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + type_t *tag_type = Table_str_get(env->types, heap_strf("%s$%s", def->name, tag->name)); + cmp_func = CORD_all(cmp_func, "\tcase $tag$", def->name, "$", tag->name, ": " + "return generic_compare(&x->", tag->name, ", &y->", tag->name, ", ", compile_type_info(env, tag_type), ");\n"); + } + cmp_func = CORD_all(cmp_func, "}\n}\n"); + return cmp_func; +} + +static CORD compile_equals_method(env_t *env, ast_t *ast) +{ + auto def = Match(ast, EnumDef); + CORD eq_func = CORD_all("static bool ", def->name, "__equal(const ", def->name, "_t *x, const ", def->name, + "_t *y, const TypeInfo *info) {\n" + "if (x->$tag != y->$tag) return no;\n" + "switch (x->$tag) {\n"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + type_t *tag_type = Table_str_get(env->types, heap_strf("%s$%s", def->name, tag->name)); + eq_func = CORD_all(eq_func, "\tcase $tag$", def->name, "$", tag->name, ": " + "return generic_equal(&x->", tag->name, ", &y->", tag->name, ", ", compile_type_info(env, tag_type), ");\n"); + } + eq_func = CORD_all(eq_func, "}\n}\n"); + return eq_func; +} + +static CORD compile_hash_method(env_t *env, ast_t *ast) +{ + auto def = Match(ast, EnumDef); + CORD hash_func = CORD_all("static uint32_t ", def->name, "__hash(const ", def->name, "_t *obj, const TypeInfo *info) {\n" + "uint32_t hashes[2] = {(uint32_t)obj->$tag};\n" + "switch (obj->$tag) {\n"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + type_t *tag_type = Table_str_get(env->types, heap_strf("%s$%s", def->name, tag->name)); + hash_func = CORD_all(hash_func, "\tcase $tag$", def->name, "$", tag->name, ": " + "hashes[1] = generic_hash(&obj->", tag->name, ", ", compile_type_info(env, tag_type), ");\n" + "break;\n"); + } + hash_func = CORD_all(hash_func, "}\n" + "uint32_t hash;\n" + "halfsiphash(&hashes, sizeof(hashes), SSS_HASH_VECTOR, (uint8_t*)&hash, sizeof(hash));\n" + "return hash;\n}\n"); + return hash_func; +} + +void compile_enum_def(env_t *env, ast_t *ast) +{ + auto def = Match(ast, EnumDef); + CORD_appendf(&env->code->typedefs, "typedef struct %s_s %s_t;\n", def->name, def->name); + CORD_appendf(&env->code->typedefs, "typedef struct { TypeInfo type; } %s_namespace_t;\n", def->name); + CORD_appendf(&env->code->typedefs, "extern %s_namespace_t %s;\n", def->name, def->name); + CORD enum_def = CORD_all("struct ", def->name, "_s {\n" + "\tenum {"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + CORD_appendf(&enum_def, "$tag$%s$%s = %ld", def->name, tag->name, tag->value); + if (tag->next) enum_def = CORD_cat(enum_def, ", "); + } + enum_def = CORD_cat(enum_def, "} $tag;\n" + "union {\n"); + for (tag_ast_t *tag = def->tags; tag; tag = tag->next) { + compile_struct_def(env, WrapAST(ast, StructDef, .name=heap_strf("%s$%s", def->name, tag->name), .fields=tag->fields)); + enum_def = CORD_all(enum_def, def->name, "$", tag->name, "_t ", tag->name, ";\n"); + CORD_appendf(&env->code->typedefs, "#define %s__%s(...) ((%s_t){$tag$%s$%s, .%s={__VA_ARGS__}})\n", + def->name, tag->name, def->name, def->name, tag->name, tag->name); + } + enum_def = CORD_cat(enum_def, "};\n};\n"); + env->code->typecode = CORD_cat(env->code->typecode, enum_def); + + type_t *t = Table_str_get(env->types, def->name); + CORD typeinfo = CORD_asprintf("public %s_namespace_t %s = {{%zu, %zu, {.tag=CustomInfo, .CustomInfo={", + def->name, def->name, type_size(t), type_align(t)); + + env->code->funcs = CORD_all( + env->code->funcs, + compile_str_method(env, ast), + compile_equals_method(env, ast), compile_compare_method(env, ast), + compile_hash_method(env, ast)); + typeinfo = CORD_all( + typeinfo, + ".as_str=(void*)", def->name, "__as_str, " + ".equal=(void*)", def->name, "__equal, " + ".hash=(void*)", def->name, "__hash, " + ".compare=(void*)", def->name, "__compare"); + typeinfo = CORD_cat(typeinfo, "}}}};\n"); + env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo); +} + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/enums.h b/enums.h new file mode 100644 index 0000000..15158ff --- /dev/null +++ b/enums.h @@ -0,0 +1,9 @@ +#pragma once +#include + +#include "ast.h" +#include "environment.h" + +void compile_enum_def(env_t *env, ast_t *ast); + +// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0 diff --git a/parse.c b/parse.c index bbdb5f3..685461d 100644 --- a/parse.c +++ b/parse.c @@ -1514,10 +1514,16 @@ ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) { spaces(&pos); arg_ast_t *fields; + bool secret = false; if (match(&pos, "(")) { whitespace(&pos); fields = parse_args(ctx, &pos, false); whitespace(&pos); + if (match(&pos, ";")) { // Extra flags + whitespace(&pos); + secret = match_word(&pos, "secret"); + whitespace(&pos); + } expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this tagged union member"); } else { fields = NULL; @@ -1535,7 +1541,7 @@ ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) { parser_err(ctx, tag_start, pos, "This tag value (%ld) is a duplicate of an earlier tag value", next_value); } - tags = new(tag_ast_t, .name=tag_name, .value=next_value, .fields=fields, .next=tags); + tags = new(tag_ast_t, .name=tag_name, .value=next_value, .fields=fields, .secret=secret, .next=tags); ++next_value; if (!match_separator(&pos)) diff --git a/structs.c b/structs.c index 154c3b3..0869bd9 100644 --- a/structs.c +++ b/structs.c @@ -152,7 +152,6 @@ void compile_struct_def(env_t *env, ast_t *ast) compile_hash_method(env, ast)); typeinfo = CORD_all( typeinfo, - ".as_str=(void*)", def->name, "__as_str, " ".equal=(void*)", def->name, "__equal, " ".hash=(void*)", def->name, "__hash, " ".compare=(void*)", def->name, "__compare"); diff --git a/tests/enums.nl b/tests/enums.nl new file mode 100644 index 0000000..485a262 --- /dev/null +++ b/tests/enums.nl @@ -0,0 +1,5 @@ +enum Foo(Zero, One(x:Int), Two(x,y:Int)) + +>> Foo__Zero() +>> Foo__One(123) +>> Foo__Two(123, 456) diff --git a/typecheck.c b/typecheck.c index eb1f242..ce3c9d0 100644 --- a/typecheck.c +++ b/typecheck.c @@ -146,8 +146,10 @@ void bind_statement(env_t *env, ast_t *statement) for (tag_t *tag = tags; tag; tag = tag->next) { const char *name = heap_strf("%s__%s", def->name, tag->name); type_t *constructor_t = Type(FunctionType, .args=Match(tag->type, StructType)->fields, .ret=type); - set_binding(env, name, new(binding_t, .type=constructor_t)); + Table_str_set(env->globals, name, new(binding_t, .type=constructor_t)); + Table_str_set(env->types, heap_strf("%s$%s", def->name, tag->name), tag->type); } + Table_str_set(env->types, def->name, type); break; } default: break;