aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CHANGES.md1
-rw-r--r--src/compile/enums.c13
-rw-r--r--src/stdlib/datatypes.h5
-rw-r--r--src/types.c4
-rw-r--r--test/enums.tm37
5 files changed, 51 insertions, 9 deletions
diff --git a/CHANGES.md b/CHANGES.md
index b16a4e8c..fb4ce929 100644
--- a/CHANGES.md
+++ b/CHANGES.md
@@ -8,6 +8,7 @@
- Library installation has been cleaned up a bit.
- List indexing now gives an optional value.
- Added support for inline anonymous enums
+- Accessing a field on an enum now gives an optional value instead of a boolean.
- Syntax for text literals and inline C code has been simplified somewhat.
- Syntax for tables has changed to use colons (`{k: v}`) instead of equals
(`{k=v}`).
diff --git a/src/compile/enums.c b/src/compile/enums.c
index ec7a1755..31af96ad 100644
--- a/src/compile/enums.c
+++ b/src/compile/enums.c
@@ -156,15 +156,20 @@ Text_t compile_enum_field_access(env_t *env, ast_t *ast) {
for (tag_t *tag = e->tags; tag; tag = tag->next) {
if (streq(f->field, tag->name)) {
Text_t tag_name = namespace_name(e->env, e->env->namespace, Texts("tag$", tag->name));
- if (fielded_t->tag == PointerType) {
+ if (tag->type != NULL && Match(tag->type, StructType)->fields) {
+ return Texts("({ ", compile_declaration(value_t, Text("_e")), " = ",
+ compile_to_pointer_depth(env, f->fielded, 0, false), "; ", "_e.$tag == ", tag_name, " ? ",
+ promote_to_optional(tag->type, Texts("_e.", tag->name)), " : ", compile_none(tag->type),
+ "; })");
+ } else if (fielded_t->tag == PointerType) {
Text_t fielded = compile_to_pointer_depth(env, f->fielded, 1, false);
- return Texts("((", fielded, ")->$tag == ", tag_name, ")");
+ return Texts("((", fielded, ")->$tag == ", tag_name, " ? OPTIONAL_EMPTY_STRUCT : NONE_EMPTY_STRUCT)");
} else if (enum_has_fields(value_t)) {
Text_t fielded = compile(env, f->fielded);
- return Texts("((", fielded, ").$tag == ", tag_name, ")");
+ return Texts("((", fielded, ").$tag == ", tag_name, " ? OPTIONAL_EMPTY_STRUCT : NONE_EMPTY_STRUCT)");
} else {
Text_t fielded = compile(env, f->fielded);
- return Texts("((", fielded, ") == ", tag_name, ")");
+ return Texts("((", fielded, ") == ", tag_name, " ? OPTIONAL_EMPTY_STRUCT : NONE_EMPTY_STRUCT)");
}
}
}
diff --git a/src/stdlib/datatypes.h b/src/stdlib/datatypes.h
index caabdacd..d51db8ab 100644
--- a/src/stdlib/datatypes.h
+++ b/src/stdlib/datatypes.h
@@ -72,11 +72,16 @@ typedef struct table_s {
typedef struct Empty$$struct {
} Empty$$type;
+#define EMPTY_STRUCT ((Empty$$type){})
+
typedef struct {
bool has_value;
Empty$$type value;
} $OptionalEmpty$$type;
+#define NONE_EMPTY_STRUCT (($OptionalEmpty$$type){.has_value = false})
+#define OPTIONAL_EMPTY_STRUCT (($OptionalEmpty$$type){.has_value = true})
+
typedef struct {
void *fn, *userdata;
} Closure_t;
diff --git a/src/types.c b/src/types.c
index 73c02807..51555560 100644
--- a/src/types.c
+++ b/src/types.c
@@ -633,7 +633,9 @@ type_t *get_field_type(type_t *t, const char *field_name) {
case EnumType: {
DeclareMatch(e, t, EnumType);
for (tag_t *tag = e->tags; tag; tag = tag->next) {
- if (streq(field_name, tag->name)) return Type(BoolType);
+ if (!streq(field_name, tag->name)) continue;
+ if (tag->type != NULL && Match(tag->type, StructType)->fields) return Type(OptionalType, tag->type);
+ else return Type(OptionalType, EMPTY_TYPE);
}
return NULL;
}
diff --git a/test/enums.tm b/test/enums.tm
index fe767ebf..80f66ed8 100644
--- a/test/enums.tm
+++ b/test/enums.tm
@@ -1,4 +1,5 @@
enum Foo(Zero, One(x:Int), Two(x:Int, y:Int), Three(x:Int, y:Text, z:Bool), Four(x,y,z,w:Int), Last(t:Text))
+enum OnlyTags(A, B, C, D)
func choose_text(f:Foo->Text)
>> f
@@ -33,10 +34,6 @@ func main()
assert Foo.One(123) == Foo.One(123)
assert Foo.Two(123, 456) == Foo.Two(x=123, y=456)
- >> one := Foo.One(123)
- assert one.One == yes
- assert one.Two == no
-
assert Foo.One(10) == Foo.One(10)
assert Foo.One(10) == Foo.Zero == no
@@ -99,3 +96,35 @@ func main()
assert EnumFields(A) == EnumFields(x=A)
+ do
+ e := OnlyTags.A
+ assert e.A == EMPTY
+ assert e.B == none
+
+ do
+ e := Foo.Zero
+ assert e.Zero == EMPTY
+ assert e.One == none
+ assert e.Two == none
+
+ ep := @Foo.Zero
+ assert ep.Zero == EMPTY
+ assert ep.One == none
+ assert ep.Two == none
+
+ do
+ e := Foo.Two(123, 456)
+ assert e.Zero == none
+ assert e.One == none
+ assert e.Two != none
+
+ ep := Foo.Two(123, 456)
+ assert ep.Zero == none
+ assert ep.One == none
+ assert ep.Two != none
+
+ two := e.Two!
+ when e is Two(x,y)
+ assert two.x == x
+ assert two.y == y
+ else fail("Unreachable")