diff options
| -rw-r--r-- | CHANGES.md | 1 | ||||
| -rw-r--r-- | src/compile/enums.c | 13 | ||||
| -rw-r--r-- | src/stdlib/datatypes.h | 5 | ||||
| -rw-r--r-- | src/types.c | 4 | ||||
| -rw-r--r-- | test/enums.tm | 37 |
5 files changed, 51 insertions, 9 deletions
@@ -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") |
