aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2024-09-11 12:29:48 -0400
committerBruce Hill <bruce@bruce-hill.com>2024-09-11 12:29:48 -0400
commit210179ee672a0c3799328a54e886f574b3823e3d (patch)
tree25ddb95503c537c19b9fdb7614df2f16c4012d21
parentdee3742b48e27ef36637d004163286d3352b0763 (diff)
Optional enums (deprecated custom tag values)
-rw-r--r--ast.c2
-rw-r--r--ast.h1
-rw-r--r--builtins/functions.c20
-rw-r--r--builtins/nextline.c2
-rw-r--r--builtins/optionals.c5
-rw-r--r--builtins/types.c4
-rw-r--r--builtins/types.h6
-rw-r--r--compile.c8
-rw-r--r--enums.c7
-rw-r--r--parse.c21
-rw-r--r--structs.c6
-rw-r--r--test/optionals.tm21
-rw-r--r--typecheck.c3
13 files changed, 58 insertions, 48 deletions
diff --git a/ast.c b/ast.c
index 869168f9..98d6f60d 100644
--- a/ast.c
+++ b/ast.c
@@ -74,7 +74,7 @@ CORD when_clauses_to_xml(when_clause_t *clauses) {
CORD tags_to_xml(tag_ast_t *tags) {
CORD c = CORD_EMPTY;
for (; tags; tags = tags->next) {
- c = CORD_all(c, "<tag name=\"", tags->name, "\" value=\"", CORD_asprintf("%ld", tags->value), "\">", arg_list_to_xml(tags->fields), "</tag>");
+ c = CORD_all(c, "<tag name=\"", tags->name, "\">", arg_list_to_xml(tags->fields), "</tag>");
}
return c;
}
diff --git a/ast.h b/ast.h
index 116f6ea7..02522a3e 100644
--- a/ast.h
+++ b/ast.h
@@ -67,7 +67,6 @@ typedef enum {
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/builtins/functions.c b/builtins/functions.c
index d6778d4c..ed0889d4 100644
--- a/builtins/functions.c
+++ b/builtins/functions.c
@@ -117,8 +117,8 @@ PUREFUNC public uint64_t generic_hash(const void *obj, const TypeInfo *type)
case OptionalInfo: {
errx(1, "Optional hash not implemented");
}
- case EmptyStruct: return 0;
- case CustomInfo:
+ case EmptyStructInfo: return 0;
+ case CustomInfo: case StructInfo: case EnumInfo: // These all share the same info
if (!type->CustomInfo.hash)
goto hash_data;
return type->CustomInfo.hash(obj, type);
@@ -142,8 +142,8 @@ PUREFUNC public int32_t generic_compare(const void *x, const void *y, const Type
case OptionalInfo: {
errx(1, "Optional compare not implemented");
}
- case EmptyStruct: return 0;
- case CustomInfo:
+ case EmptyStructInfo: return 0;
+ case CustomInfo: case StructInfo: case EnumInfo: // These all share the same info
if (!type->CustomInfo.compare)
goto compare_data;
return type->CustomInfo.compare(x, y, type);
@@ -163,11 +163,11 @@ PUREFUNC public bool generic_equal(const void *x, const void *y, const TypeInfo
case ArrayInfo: return Array$equal(x, y, type);
case ChannelInfo: return Channel$equal((const channel_t**)x, (const channel_t**)y, type);
case TableInfo: return Table$equal(x, y, type);
- case EmptyStruct: return true;
+ case EmptyStructInfo: return true;
case OptionalInfo: {
errx(1, "Optional equal not implemented");
}
- case CustomInfo:
+ case CustomInfo: case StructInfo: case EnumInfo: // These all share the same info
if (!type->CustomInfo.equal)
goto use_generic_compare;
return type->CustomInfo.equal(x, y, type);
@@ -188,10 +188,10 @@ public Text_t generic_as_text(const void *obj, bool colorize, const TypeInfo *ty
case TableInfo: return Table$as_text(obj, colorize, type);
case TypeInfoInfo: return Type$as_text(obj, colorize, type);
case OptionalInfo: return Optional$as_text(obj, colorize, type);
- case EmptyStruct: return colorize ?
- Text$concat(Text("\x1b[0;1m"), Text$from_str(type->EmptyStruct.name), Text("\x1b[m()"))
- : Text$concat(Text$from_str(type->EmptyStruct.name), Text("()"));
- case CustomInfo:
+ case EmptyStructInfo: return colorize ?
+ Text$concat(Text("\x1b[0;1m"), Text$from_str(type->EmptyStructInfo.name), Text("\x1b[m()"))
+ : Text$concat(Text$from_str(type->EmptyStructInfo.name), Text("()"));
+ case CustomInfo: case StructInfo: case EnumInfo: // These all share the same info
if (!type->CustomInfo.as_text)
fail("No text function provided for type!\n");
return type->CustomInfo.as_text(obj, colorize, type);
diff --git a/builtins/nextline.c b/builtins/nextline.c
index 3fa37412..b7928939 100644
--- a/builtins/nextline.c
+++ b/builtins/nextline.c
@@ -91,7 +91,7 @@ static uint64_t NextLine$hash(const NextLine_t *obj, const TypeInfo *info)
return siphash24((void *) &hashes, sizeof(hashes));
}
-public const TypeInfo NextLine$Done = { 0, 0, {.tag = EmptyStruct,.EmptyStruct.name =
+public const TypeInfo NextLine$Done = { 0, 0, {.tag = EmptyStructInfo,.EmptyStructInfo.name =
"NextLine$Done" } };
public const TypeInfo NextLine$Next = { 24, 8, {.tag = CustomInfo,.CustomInfo =
{.as_text =
diff --git a/builtins/optionals.c b/builtins/optionals.c
index f58c8134..88f08097 100644
--- a/builtins/optionals.c
+++ b/builtins/optionals.c
@@ -36,12 +36,15 @@ static inline bool is_null(const void *obj, const TypeInfo *non_optional_type)
case ArrayInfo: return ((Array_t*)obj)->length < 0;
case TableInfo: return ((Table_t*)obj)->entries.length < 0;
case FunctionInfo: return *(void**)obj == NULL;
- case CustomInfo: {
+ case StructInfo: {
int64_t offset = non_optional_type->size;
if (offset % non_optional_type->align)
offset += non_optional_type->align - (offset % non_optional_type->align);
return *(bool*)(obj + offset);
}
+ case EnumInfo: {
+ return (*(int*)obj) == 0; // NULL tag
+ }
default: {
Text_t t = generic_as_text(NULL, false, non_optional_type);
errx(1, "is_null() not implemented for: %k", &t);
diff --git a/builtins/types.c b/builtins/types.c
index 79f65f71..512fdfc3 100644
--- a/builtins/types.c
+++ b/builtins/types.c
@@ -32,8 +32,8 @@ public const TypeInfo TypeInfo$info = {
.TypeInfoInfo.type_str="TypeInfo",
};
-public const TypeInfo Void$info = {.size=0, .align=0, .tag=EmptyStruct};
-public const TypeInfo Abort$info = {.size=0, .align=0, .tag=EmptyStruct};
+public const TypeInfo Void$info = {.size=0, .align=0, .tag=EmptyStructInfo};
+public const TypeInfo Abort$info = {.size=0, .align=0, .tag=EmptyStructInfo};
public Text_t Func$as_text(const void *fn, bool colorize, const TypeInfo *type)
{
diff --git a/builtins/types.h b/builtins/types.h
index a6cc40c6..38d8bdc5 100644
--- a/builtins/types.h
+++ b/builtins/types.h
@@ -17,8 +17,8 @@ typedef Text_t (*text_fn_t)(const void*, bool, const struct TypeInfo*);
typedef struct TypeInfo {
int64_t size, align;
struct { // Anonymous tagged union for convenience
- enum { CustomInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo,
- OptionalInfo, TypeInfoInfo, OpaqueInfo, EmptyStruct } tag;
+ enum { CustomInfo, StructInfo, EnumInfo, PointerInfo, TextInfo, ArrayInfo, ChannelInfo, TableInfo, FunctionInfo,
+ OptionalInfo, TypeInfoInfo, OpaqueInfo, EmptyStructInfo } tag;
union {
struct {
equal_fn_t equal;
@@ -52,7 +52,7 @@ typedef struct TypeInfo {
struct {} OpaqueInfo;
struct {
const char *name;
- } EmptyStruct;
+ } EmptyStructInfo;
};
};
} TypeInfo;
diff --git a/compile.c b/compile.c
index 5d46e67c..cc3603ae 100644
--- a/compile.c
+++ b/compile.c
@@ -222,7 +222,7 @@ CORD compile_type(type_t *t)
switch (nonnull->tag) {
case BoolType: case CStringType: case BigIntType: case NumType: case TextType:
case ArrayType: case SetType: case TableType: case FunctionType: case ClosureType:
- case PointerType:
+ case PointerType: case EnumType:
return compile_type(nonnull);
case IntType:
return CORD_all("Optional", compile_type(nonnull));
@@ -348,6 +348,8 @@ static CORD compile_optional_check(env_t *env, ast_t *ast)
return CORD_all("!(", compile(env, ast), ").is_null");
else if (t->tag == StructType)
return CORD_all("!(", compile(env, ast), ").is_null");
+ else if (t->tag == EnumType)
+ return CORD_all("((", compile(env, ast), ").tag != 0)");
errx(1, "Optional check not implemented for: %T", t);
}
@@ -1707,6 +1709,10 @@ CORD compile(env_t *env, ast_t *ast)
case ClosureType: return "NULL_CLOSURE";
case NumType: return "nan(\"null\")";
case StructType: return CORD_all("((", compile_type(Type(OptionalType, .type=t)), "){.is_null=true})");
+ case EnumType: {
+ env_t *enum_env = Match(t, EnumType)->env;
+ return CORD_all("((", compile_type(t), "){", namespace_prefix(enum_env->libname, enum_env->namespace), "null})");
+ }
default: code_err(ast, "Nil isn't implemented for this type: %T", t);
}
}
diff --git a/enums.c b/enums.c
index aa26e86a..a0817b01 100644
--- a/enums.c
+++ b/enums.c
@@ -171,7 +171,7 @@ void compile_enum_def(env_t *env, ast_t *ast)
}
type_t *t = Table$str_get(*env->types, def->name);
- CORD typeinfo = CORD_asprintf("public const TypeInfo %s = {%zu, %zu, {.tag=CustomInfo, .CustomInfo={",
+ CORD typeinfo = CORD_asprintf("public const TypeInfo %s = {%zu, %zu, {.tag=EnumInfo, .CustomInfo={",
full_name, type_size(t), type_align(t));
env->code->funcs = CORD_all(env->code->funcs, compile_str_method(env, ast));
@@ -202,9 +202,10 @@ CORD compile_enum_typedef(env_t *env, ast_t *ast)
CORD full_name = CORD_cat(namespace_prefix(env->libname, env->namespace), def->name);
CORD all_defs = CORD_all("typedef struct ", full_name, "_s ", full_name, "_t;\n");
CORD enum_def = CORD_all("struct ", full_name, "_s {\n"
- "\tenum {");
+ "\tenum { ", full_name, "$null=0, ");
+
for (tag_ast_t *tag = def->tags; tag; tag = tag->next) {
- CORD_appendf(&enum_def, "%r$tag$%s = %ld", full_name, tag->name, tag->value);
+ enum_def = CORD_all(enum_def, full_name, "$tag$", tag->name);
if (tag->next) enum_def = CORD_cat(enum_def, ", ");
}
enum_def = CORD_cat(enum_def, "} tag;\n"
diff --git a/parse.c b/parse.c
index 6191ceaf..b0717f8c 100644
--- a/parse.c
+++ b/parse.c
@@ -2028,12 +2028,8 @@ ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) {
if (!match(&pos, "(")) return NULL;
tag_ast_t *tags = NULL;
- int64_t next_value = 0;
-
whitespace(&pos);
for (;;) {
- const char *tag_start = pos;
-
spaces(&pos);
const char *tag_name = get_id(&pos);
if (!tag_name) break;
@@ -2055,22 +2051,7 @@ ast_t *parse_enum_def(parse_ctx_t *ctx, const char *pos) {
fields = NULL;
}
- spaces(&pos);
- if (match(&pos, "=")) {
- ast_t *val = expect(ctx, tag_start, &pos, parse_int, "I expected an integer literal after this '='");
- Int_t i = Int$from_text(Text$from_str(Match(val, Int)->str), NULL);
- // TODO check for overflow
- next_value = (i.small >> 2);
- }
-
- // Check for duplicate values:
- for (tag_ast_t *t = tags; t; t = t->next) {
- if (t->value == next_value)
- 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, .secret=secret, .next=tags);
- ++next_value;
+ tags = new(tag_ast_t, .name=tag_name, .fields=fields, .secret=secret, .next=tags);
if (!match_separator(&pos))
break;
diff --git a/structs.c b/structs.c
index ab5efafb..cc3de838 100644
--- a/structs.c
+++ b/structs.c
@@ -121,7 +121,7 @@ void compile_struct_def(env_t *env, ast_t *ast)
assert(t && t->tag == StructType);
auto struct_ = Match(t, StructType);
if (def->fields) {
- CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=CustomInfo, .CustomInfo={",
+ CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=StructInfo, .CustomInfo={",
full_name, type_size(t), type_align(t));
typeinfo = CORD_all(typeinfo, ".as_text=(void*)", full_name, "$as_text, ");
@@ -156,8 +156,8 @@ void compile_struct_def(env_t *env, ast_t *ast)
typeinfo = CORD_cat(typeinfo, "}}};\n");
env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
} else {
- // If there are no fields, we can use an EmptyStruct typeinfo, which generates less code:
- CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=EmptyStruct, .EmptyStruct.name=%r}};\n",
+ // If there are no fields, we can use an EmptyStructInfo typeinfo, which generates less code:
+ CORD typeinfo = CORD_asprintf("public const TypeInfo %r = {%zu, %zu, {.tag=EmptyStructInfo, .EmptyStructInfo.name=%r}};\n",
full_name, type_size(t), type_align(t), CORD_quoted(def->name));
env->code->typeinfos = CORD_all(env->code->typeinfos, typeinfo);
}
diff --git a/test/optionals.tm b/test/optionals.tm
index b80fbab0..a721deaa 100644
--- a/test/optionals.tm
+++ b/test/optionals.tm
@@ -1,11 +1,17 @@
struct Struct(x:Int, y:Text):
- func maybe(should_i:Bool)-> Struct?:
+ func maybe(should_i:Bool)->Struct?:
if should_i:
return Struct(123, "hello")
else:
return !Struct
+enum Enum(X, Y(y:Int)):
+ func maybe(should_i:Bool)->Enum?:
+ if should_i:
+ return Enum.Y(123)
+ else:
+ return !Enum
func maybe_int(should_i:Bool)->Int?:
if should_i:
@@ -143,6 +149,19 @@ func main():
fail("Truthy: $nope")
else: !! Falsey: $nope
+ do:
+ !! ...
+ !! Enums:
+ >> yep := Enum.maybe(yes)
+ = Enum.Y(y=123)?
+ >> nope := Enum.maybe(no)
+ = !Enum
+ >> if yep: >> yep
+ else: fail("Falsey: $yep")
+ >> if nope:
+ fail("Truthy: $nope")
+ else: !! Falsey: $nope
+
if yep := maybe_int(yes):
>> yep
= 123 : Int
diff --git a/typecheck.c b/typecheck.c
index 5d0e2b48..dabd5532 100644
--- a/typecheck.c
+++ b/typecheck.c
@@ -324,6 +324,7 @@ void bind_statement(env_t *env, ast_t *statement)
type_t *type = Table$str_get(*env->types, def->name);
assert(type);
tag_t *tags = NULL;
+ int64_t next_tag = 1;
for (tag_ast_t *tag_ast = def->tags; 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) {
@@ -342,7 +343,7 @@ void bind_statement(env_t *env, ast_t *statement)
REVERSE_LIST(fields);
env_t *member_ns = namespace_env(env, heap_strf("%s$%s", def->name, tag_ast->name));
type_t *tag_type = Type(StructType, .name=heap_strf("%s$%s", def->name, tag_ast->name), .fields=fields, .env=member_ns);
- tags = new(tag_t, .name=tag_ast->name, .tag_value=tag_ast->value, .type=tag_type, .next=tags);
+ tags = new(tag_t, .name=tag_ast->name, .tag_value=(next_tag++), .type=tag_type, .next=tags);
}
REVERSE_LIST(tags);
type->__data.EnumType.tags = tags;